HDU 4436 str2int 后缀数组(前缀和预处理)
2015-03-10 17:49
411 查看
题意:
给定n个包含(‘0’~‘9’)的字符串,求字符串不同子串的和。
首先对于n个字符串,我们通常是要用其他字符将其连成一个字符串的,这样便于用后缀数组进行处理。
其次,求不同子串,数量是n-sa[i]+1-height[i]. 新子串自然就从sa[i]~sa[i]+height[i]开始到,sa[i]~当前字符串结束为止(其他字符之前)为止,因为下一个就会包含两个字符串。
对于每个子串,只要不是0开头或者其他字符开头,那么这个子串就是有意义的。累加新子串的权值就是答案。但是一个一个的算显然会超时。这时候我们需要用前缀和来进行预处理。达到o(1)的计算时间。
假设字符串为s1s2s3.....sn.
cur[i]表示s1...si-1的权值。
sig[i]表示s1+s1s2+s1s2s3+...+s1s2s3..si-1的权值,也就是cur的前缀和。
ten[i]表示sum(10^j)0<=j<i;
sa[i]~sta....sa[i]~end为有用的新串。
那么权值W=sig[end]-sig[sta]+MOD-cur[sa[i]]*(ten[end-sa[i]+1]-ten[sta-sa[i]+1]+MOD)%MOD;
附代码喵:
给定n个包含(‘0’~‘9’)的字符串,求字符串不同子串的和。
首先对于n个字符串,我们通常是要用其他字符将其连成一个字符串的,这样便于用后缀数组进行处理。
其次,求不同子串,数量是n-sa[i]+1-height[i]. 新子串自然就从sa[i]~sa[i]+height[i]开始到,sa[i]~当前字符串结束为止(其他字符之前)为止,因为下一个就会包含两个字符串。
对于每个子串,只要不是0开头或者其他字符开头,那么这个子串就是有意义的。累加新子串的权值就是答案。但是一个一个的算显然会超时。这时候我们需要用前缀和来进行预处理。达到o(1)的计算时间。
假设字符串为s1s2s3.....sn.
cur[i]表示s1...si-1的权值。
sig[i]表示s1+s1s2+s1s2s3+...+s1s2s3..si-1的权值,也就是cur的前缀和。
ten[i]表示sum(10^j)0<=j<i;
sa[i]~sta....sa[i]~end为有用的新串。
那么权值W=sig[end]-sig[sta]+MOD-cur[sa[i]]*(ten[end-sa[i]+1]-ten[sta-sa[i]+1]+MOD)%MOD;
附代码喵:
#include<cstdio> #include<cstring> using namespace std; const int maxn=111000; const int MOD=2012; char s[maxn],key[maxn]; int sa[maxn],c[maxn],wa[maxn],wb[maxn]; int sig[maxn],cur[maxn],ten[maxn]; int end[maxn]; bool cmp(int *r,int a,int b,int k){ return r[a]==r[b]&&r[a+k]==r[b+k]; } void build_sa(int m,int n) { int *x=wa,*y=wb,*t; for(int i=0;i<m;i++)c[i]=0; for(int i=0;i<n;i++)c[x[i]=s[i]]++; for(int i=1;i<m;i++)c[i]+=c[i-1]; for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i; for(int k=1;k<n;k<<=1){ int p=0; for(int i=n-k;i<n;i++)y[p++]=i; for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; for(int i=0;i<m;i++)c[i]=0; for(int i=0;i<n;i++)c[x[y[i]]]++; for(int i=1;i<m;i++)c[i]+=c[i-1]; for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; t=x;x=y;y=t; x[sa[0]]=0; p=1; for(int i=1;i<n;i++) { x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++; } if(p>=n)break; m=p; } } int height[maxn],ran[maxn]; void calheight(int n){ for(int i=0;i<n;i++)ran[sa[i]]=i; int k=0; for(int i=0;i<n;i++) { if(k)k--; if(ran[i]==0)continue; int j=sa[ran[i]-1]; while(s[j+k]==s[i+k])k++; height[ran[i]]=k; } } void solve(int n) { int sum=0; for(int i=1;i<n;i++) { if(s[sa[i]]==1||s[sa[i]]==11)continue;//0、11开头的不用计算。 int sta=sa[i]+height[i],tail=end[sa[i]]; if(sta>=tail)continue; sum=(sum+sig[tail]-sig[sta]+MOD-cur[sa[i]]*(ten[tail-sa[i]+1]-ten[sta-sa[i]+1]+MOD)%MOD+MOD)%MOD; } printf("%d\n",sum); } void init(int n) { memset(cur,0,sizeof(cur)); memset(sig,0,sizeof(sig)); memset(ten,0,sizeof(ten)); cur[0]=0;sig[0]=0; cur[1]=(s[0]-1)%MOD; sig[1]=cur[1]; int tmp=1; ten[1]=tmp; for(int i=1;i<n;i++){ cur[i+1]=(cur[i]*10+s[i]-1)%MOD; sig[i+1]=(sig[i]+cur[i+1])%MOD; tmp=tmp*10%MOD; ten[i+1]=(ten[i]+tmp)%MOD; } } int main() { int t; while(scanf("%d",&t)!=EOF){ memset(end,0,sizeof(end)); memset(sa,0,sizeof(sa)); memset(s,0,sizeof(s)); int n=0,temp=0; while(t--){ scanf("%s",key); temp=n; n+=strlen(key); for(int i=temp,j=0;i<n;i++,j++) { end[i]=n; s[i]=key[j]-'0'+1; } if(t>0)s[n++]=11; } s =0; build_sa(12,n+1); calheight(n+1); init(n+1); solve(n+1); } return 0; }
相关文章推荐
- HDU 4436 str2int 后缀数组 + 前缀和预处理 或 后缀自动机
- HDU 4436 str2int 后缀数组 字符串哈希 前缀和
- hdu 4436 str2int 后缀数组
- hdu 4436 str2int(后缀数组)
- HDU 4436 str2int(后缀自动机)
- HDU 4436 str2int (后缀自动机SAM,多串建立)
- HDU 4436 str2int
- hdu 5317 (普通素数筛的应用)+(前缀和预处理)
- hdu 4436 str2int 后缀数组、后缀自动机
- hdu 4436 str2int (SAM)(待补)
- HDU 4436 str2int(后缀自动机)(2012 Asia Tianjin Regional Contest)
- HDU 4436 str2int
- HDU 4436 str2int(后缀数组,一种统计n个digit字符串所有不同子串之和的方法)
- HDU 4436 str2int
- hdu 4436 str2int (SAM)
- 后缀数组 HDU 4436
- hdu 4436 str2int(后缀自动机)
- hdu1403 求最长公共前缀 后缀数组
- HDU 4436 后缀数组
- HDU 2089 不要62 预处理+前缀和