读书人

HDU str2int 前缀跟的力量

发布时间: 2012-11-08 08:48:12 作者: rapoo

HDU str2int 前缀和的力量

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;const int maxn=200050;const int mod=2012;//以下为倍增算法求后缀数组int wa[maxn],wb[maxn],wv[maxn],Ws[maxn];int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}void da(const int *r,int *sa,int n,int m){    int i,j,p,*x=wa,*y=wb,*t;     for(i=0;i<m;i++) Ws[i]=0;     for(i=0;i<n;i++) Ws[x[i]=r[i]]++;     for(i=1;i<m;i++) Ws[i]+=Ws[i-1];     for(i=n-1;i>=0;i--) sa[--Ws[x[i]]]=i;     for(j=1,p=1;p<n;j*=2,m=p){         for(p=0,i=n-j;i<n;i++) y[p++]=i;         for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;         for(i=0;i<n;i++) wv[i]=x[y[i]]; //x[]->上次排序的排名        for(i=0;i<m;i++) Ws[i]=0;         for(i=0;i<n;i++) Ws[wv[i]]++;         for(i=1;i<m;i++) Ws[i]+=Ws[i-1];         for(i=n-1;i>=0;i--) sa[--Ws[wv[i]]]=y[i];         //y[i]->二级排序后第i大的下标,循环从n-1到0由于一级排序相同要看二级排序        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)             x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;     }     return; }int sa[maxn],Rank[maxn],height[maxn];//求height数组//sa[1,n]取值范围[0,n-1],sa[0]为特殊字符//Rank[0,n-1]取值范围[1,n]//height[1,n]表示排名相邻的两个后缀的最长公共前缀//height[i]表示sa[i]和sa[i-1]的最长前缀 排名为i与i-1的最长公共前缀void calheight(const int *r,int *sa,int n){    int i,j,k=0;    for(i=1;i<=n;i++) Rank[sa[i]]=i;    for(i=0;i<n;height[Rank[i++]]=k)        for(k?k--:0,j=sa[Rank[i]-1];r[i+k]==r[j+k];k++);    return;}/*da(r,sa,n+1,128);n+1表示加入特殊字符后的长度[0,n];calheight(r,sa,n);去掉特殊字符[1,n]长度,就是输入字符串长度n*/int pos[maxn];char ch[maxn];int a[maxn];int dig[maxn];int Pow[maxn];int lsum[maxn];int sum[maxn];int ID[maxn];int p[maxn];void init(){    Pow[0]=0;p[0]=1;    for(int i=1;i<maxn;i++){        Pow[i]=((Pow[i-1]+1)*10)%mod;p[i]=p[i-1]*10%mod;}}int Min(int a,int b){return a<b?a:b;}int Gao(int n){    int i,j,k,l,r,mid;    int id=0,ret=0;    memset(lsum,0,sizeof(lsum));    memset(sum,0,sizeof(sum));    while(id<n){        l=id;r=ID[id]-1;        lsum[l]=dig[l];        for(i=id+1;i<=r;i++){            lsum[i]=(lsum[i-1]*10+dig[i])%mod;//求前缀数        }        sum[r]=lsum[r];        for(i=r-1;i>=l;i--){            sum[i]=(sum[i+1]+lsum[i])%mod;//求前缀和        }        id=ID[id];    }    for(i=1;i<=n;i++){        l=sa[i];r=ID[sa[i]]-1;        if(!dig[sa[i]])continue;        mid=height[i];        if(mid+l-1>r)mid=r-l+1;        int x=l+mid;if(x<=r){ret=(ret+sum[x])%mod;}if(l-1>=pos[l] && x<=r){/*后缀的前缀就是子串st 表示原串开始位置 ed原串终点位置st l  x  ed 我们要求[l,ed]去掉重复的,即前面求过的[l,x]即ret+sum[x],在减去多算的[st,l-1],值t=lsum[l-1]*p[x-1],Pow[r-x+1]表示t的10^k的多算个数*/ret=(ret-(lsum[l-1]*p[x-l]%mod)*Pow[r-x+1])%mod;//}    }    return (ret+mod)%mod;}int main(){int dif;    int n,m;    int i,j,k;    init();    while(scanf("%d",&m)!=EOF && m){        n=0;dif=12;/*不加dif,是字符串隔开,无法按字典排序去重如a ->111b ->11组成一个111 11字符串a 1a 11b 1 a 11b 11 a 11b 111 a 11  */memset(dig,0,sizeof(dig));        for(i=0;i<m;i++){            scanf("%s",ch);            int len=strlen(ch);            int cur=n;            for(j=0;j<len;j++){                a[n]=ch[j]-'0'+1;                ID[n]=len+cur;pos[n]=cur;                dig[n++]=ch[j]-'0';            }pos[n]=n;ID[n]=n+1;a[n++]=dif++;        }ID[n]=n+1;pos[n]=n;        a[n]=0;        da(a,sa,n+1,dif);        calheight(a,sa,n);        printf("%d\n",Gao(n));    }    return 0;}/*21111160000000000011222545451254954948312165415151545674874231242142245454456485745331313343  */

读书人网 >软件架构设计

热点推荐