首页 > 代码库 > 后缀数组
后缀数组
做个专题,好好研究一下;
1.[后缀数组预备知识]基数排序
/*chad*/ #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<ctime> #include<string> using namespace std; const int maxn(100010),inf(1000000000); #define FILE "dealing" #define LL long long #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++) namespace IO{ char buf[1<<15],*fs,*ft; int gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} LL read(){ LL x=0,ch=gc(),f=0; while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=1;ch=gc();} while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=gc();} return f?-x:x; } }using namespace IO; int n; char s[maxn][12]; int c[maxn],m=130,x[maxn],sa[maxn],y[maxn]; int main(){ scanf("%d",&n); up(i,1,n)scanf("%s",s[i]); for(int i=1;i<=n;i++)y[i]=i; for(int k=9;k>=0;k--){ for(int i=0;i<=m;i++)c[i]=0; for(int i=1;i<=n;i++)c[s[i][k]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[s[y[i]][k]]--]=y[i];//sa[i]表示sa[i]个字符串在第i位 for(int i=1;i<=n;i++) y[i]=sa[i]; } up(i,1,n)printf("%d ",sa[i]); return 0; }
2.[后缀数组]基因突变
最近,jzyz的科学家忽然发现了一种神秘的生物出现在了霞栖湖中,通过提取DNA,科学家发现这个生物的DNA由a.....z共26种碱基对组成,而且这个生物常常容易发生DNA片段的缺失。那么问题来了。科学家想知道DNA片段的缺失对这个生物会产生什么影响。
给你一段长为N的DNA序列(保证全为小写字母),请求出从x到y-1的片段缺失后,忽略前x-1的长度,他们最长还有多长连续序列是相同的?
这题的主要难度在于读懂题目;
实质是求lcp;
1 /*chad*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 using namespace std; 9 const int maxn(300005),inf(100000000); 10 #define FILE "dealing" 11 #define LL long long 12 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++) 13 namespace IO{ 14 char buf[1<<15],*fs,*ft; 15 int gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} 16 int read(){ 17 int x=0,ch=gc(),f=0; 18 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=1;ch=gc();} 19 while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=gc();} 20 return f?-x:x; 21 } 22 }using namespace IO; 23 char s[maxn]; 24 int n,limit; 25 int t1[maxn],t2[maxn],c[maxn],sa[maxn]; 26 void getsa(int m){ 27 int *x=t1,*y=t2; 28 for(int i=0;i<m;i++)c[i]=0; 29 for(int i=0;i<n;i++)c[x[i]=s[i]]++; 30 for(int i=1;i<m;i++)c[i]+=c[i-1]; 31 for(int i=n-1;i>=0;i--) 32 sa[--c[x[i]]]=i; 33 for(int k=1,p=0;k<=n;k<<=1){ 34 p=0; 35 for(int i=n-k;i<n;i++)y[p++]=i; 36 for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; 37 for(int i=0;i<m;i++)c[i]=0; 38 for(int i=0;i<n;i++)c[x[y[i]]]++; 39 for(int i=0;i<m;i++)c[i]+=c[i-1]; 40 for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; 41 swap(x,y); 42 x[sa[0]]=0;p=1; 43 for(int i=1;i<n;i++) 44 x[sa[i]]= (y[sa[i]]==y[sa[i-1]]&&y[sa[i-1]+k]==y[sa[i]+k])?p-1:p++; 45 if(p>=n)break; 46 m=p; 47 } 48 } 49 int rank[maxn],height[maxn]; 50 void getheight(){ 51 int i,j,k=0; 52 for(i=0;i<n;i++)rank[sa[i]]=i; 53 for(i=0;i<n;i++){ 54 if(k)k--; 55 if(rank[i]-1<0)continue; 56 j=sa[rank[i]-1]; 57 while(s[j+k]==s[i+k])k++; 58 height[rank[i]]=k; 59 } 60 } 61 int d[maxn][30]; 62 int main(){ 63 //freopen("dealing.in","r",stdin); 64 //freopen("dealing.out","w",stdout); 65 scanf("%d",&n);n++;limit=(int)log2(n*1.0+1)+1; 66 scanf("%s",s); 67 int q,x,y; 68 scanf("%d",&q); 69 getsa(300); 70 getheight(); 71 memset(d,10,sizeof(d)); 72 for(int i=0;i<n;i++)d[i][0]=height[i+1]; 73 for(int j=1;j<=limit;j++) 74 for(int i=0;i<n;i++) 75 if(i+(1<<j-1)<n)d[i][j]=min(d[i][j-1],d[i+(1<<j-1)][j-1]); 76 while(q--){ 77 scanf("%d%d",&x,&y); 78 if(x==y){printf("%d\n",n-x);continue;} 79 x=rank[x-1],y=rank[y-1]; 80 if(x>y)swap(x,y); 81 int ans=inf; 82 for(int i=limit;i>=0;i--) 83 if(x+(1<<i)<=y)ans=min(ans,d[x][i]),x=x+(1<<i); 84 printf("%d\n",ans); 85 } 86 return 0; 87 }
3.
第三次世界大战爆发期间,美德两军同盟,为了传递军事机密,两方约定以相关的秘钥进行密文的破译,尽管这样,他们仍然担心情报的安全性,于是约定对于一段密文,只有密文中最长重复的片段是有效且可翻译的情报(最长的重复片段可以重叠),例如对于这样一段长度N=5的密文:ababa,只有aba是有效密文,他们需要设计一个程序来找到这一段有效密文。
为了方便对比,只输出找出最长子串的长度即可。
对于同一个字符串来说,height数组里的max值即为答案;
1 /*chad*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 #include<ctime> 9 #include<string> 10 using namespace std; 11 const int maxn(100010),inf(1000000000); 12 #define FILE "dealing" 13 #define LL long long 14 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++) 15 namespace IO{ 16 char buf[1<<15],*fs,*ft; 17 LL gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} 18 LL read(){ 19 LL x=0,ch=gc(),f=0; 20 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=1;ch=gc();} 21 while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=gc();} 22 return f?-x:x; 23 } 24 }using namespace IO; 25 int n; 26 char s[maxn]; 27 int t1[maxn],t2[maxn],c[maxn],rank[maxn],height[maxn],sa[maxn]; 28 void getsa(int m){ 29 int *x=t1,*y=t2; 30 for(int i=0;i<m;i++)c[i]=0; 31 for(int i=0;i<n;i++)c[x[i]=s[i]]++; 32 for(int i=1;i<m;i++)c[i]+=c[i-1]; 33 for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i; 34 for(int k=1,p;k<=n;k<<=1){ 35 p=0; 36 for(int i=n-k;i<n;i++)y[p++]=i; 37 for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; 38 for(int i=0;i<m;i++)c[i]=0; 39 for(int i=0;i<n;i++)c[x[y[i]]]++; 40 for(int i=1;i<m;i++)c[i]+=c[i-1]; 41 for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; 42 swap(x,y); 43 x[sa[0]]=0;p=1; 44 for(int i=1;i<n;i++) 45 x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i-1]+k]==y[sa[i]+k])?p-1:p++; 46 if(p>=n)break; 47 m=p; 48 } 49 } 50 void getheight(){ 51 int i,j,k=0; 52 for(int i=0;i<n;i++)rank[sa[i]]=i; 53 for(int i=0;i<n;i++){ 54 if(k)k--; 55 if(rank[i]-1<0)continue; 56 j=sa[rank[i]-1]; 57 while(s[j+k]==s[i+k])k++; 58 height[rank[i]]=k; 59 } 60 } 61 int main(){ 62 scanf("%d",&n);n++; 63 scanf("%s",s); 64 getsa(300); 65 getheight(); 66 int ans=0; 67 for(int i=1;i<n;i++)ans=max(height[i],ans); 68 cout<<ans<<endl; 69 return 0; 70 }
4.可重叠的最少重复k次的最长子串
二分一下最长长度,再判断一下是否重复k次以上即可;
1 /*chad*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 #include<ctime> 9 #include<string> 10 using namespace std; 11 const int maxn(1000010),inf(1000000000); 12 #define FILE "dealing" 13 #define LL long long 14 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++) 15 namespace IO{ 16 char buf[1<<15],*fs,*ft; 17 LL gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} 18 LL read(){ 19 LL x=0,ch=gc(),f=0; 20 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=1;ch=gc();} 21 while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=gc();} 22 return f?-x:x; 23 } 24 }using namespace IO; 25 int n,k; 26 int s[maxn],c[maxn],rank[maxn],height[maxn],t1[maxn],t2[maxn],sa[maxn]; 27 bool chkmax(int &a,int b){return a<b?(a=b,true):false;} 28 void getsa(int m){ 29 int *x=t1,*y=t2; 30 for(int i=0;i<m;i++)c[i]=0; 31 for(int i=0;i<n;i++)c[x[i]=s[i]]++; 32 for(int i=1;i<m;i++)c[i]+=c[i-1]; 33 for(int i=0;i<n;i++)sa[--c[x[i]]]=i; 34 for(int k=1,p;k<=n;k<<=1){ 35 p=0; 36 for(int i=n-k;i<n;i++)y[p++]=i; 37 for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; 38 for(int i=0;i<m;i++)c[i]=0; 39 for(int i=0;i<n;i++)c[x[y[i]]]++; 40 for(int i=1;i<m;i++)c[i]+=c[i-1]; 41 for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; 42 swap(x,y); 43 x[sa[0]]=0;p=1; 44 for(int i=1;i<n;i++) 45 x[sa[i]]= y[sa[i]]==y[sa[i-1]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++; 46 if(p>=n)break; 47 m=p; 48 } 49 } 50 void getheight(){ 51 int i,j,k=0; 52 for(i=0;i<n;i++)rank[sa[i]]=i; 53 for(i=0;i<n;i++){ 54 if(k)k--; 55 if(rank[i]-1<0)continue; 56 j=sa[rank[i]-1]; 57 while(s[j+k]==s[i+k])k++; 58 height[rank[i]]=k; 59 } 60 } 61 int ch[maxn][2],top=0; 62 bool find(int mid){ 63 int ans=0,sum=1; 64 for(int i=1;i<n;i++){ 65 chkmax(ans,sum); 66 if(height[i]<mid)sum=1; 67 else sum++; 68 } 69 return ans>=k; 70 } 71 int main(){ 72 n=read(),k=read(); 73 up(i,0,n-1)s[i]=read();s[n]=1000001;n++; 74 getsa(1000002); 75 getheight(); 76 int left=0,right=1010000,mid,ans; 77 while(left<=right){ 78 mid=(left+right)>>1; 79 if(find(mid))ans=mid,left=mid+1; 80 else right=mid-1; 81 } 82 cout<<ans<<endl; 83 return 0; 84 }
5.
很早很早很早以前,在语言还没有出现时,人们的交流很困难。他们渐渐觉得应该用几个连续组合的符号来表示一件物品,于是他们想通过打造一个单词生成器来帮助他们完成这个工作(雾),身为酋长,请你帮他们完成这个工作。
我们规定单词生成器的原理是这样的,对于一段字符串,比如CCCCC,按照从左到右的顺序连续抽取若干个字符,来组成一个新的单词,比如说上面那个例子对应的所有可生成的单词有 C CC CCC CCCC CCCCC,共五个,我们想知道对于一串字符串,最多可以生成多少个互不相同的单词.
求一个字符串有多少个子串,由于height数组已排序,在height数组里顺序扫一遍即可;
1 /*chad*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 #include<ctime> 9 #include<string> 10 using namespace std; 11 const int maxn(1010),inf(1000000000); 12 #define FILE "dealing" 13 #define LL long long 14 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++) 15 namespace IO{ 16 char buf[1<<15],*fs,*ft; 17 LL gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} 18 LL read(){ 19 LL x=0,ch=gc(),f=0; 20 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=1;ch=gc();} 21 while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=gc();} 22 return f?-x:x; 23 } 24 }using namespace IO; 25 int n; 26 char s[maxn]; 27 int rank[maxn],height[maxn],sa[maxn],t1[maxn],t2[maxn],c[maxn]; 28 void getsa(int m){ 29 int *x=t1,*y=t2; 30 for(int i=0;i<m;i++)c[i]=0; 31 for(int i=0;i<n;i++)c[x[i]=s[i]]++; 32 for(int i=1;i<m;i++)c[i]+=c[i-1]; 33 for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i; 34 for(int k=1,p;k<=n;k<<=1){ 35 p=0; 36 for(int i=n-k;i<n;i++)y[p++]=i; 37 for(int i=0;i<n;i++)if(sa[i]-k>=0)y[p++]=sa[i]-k; 38 for(int i=0;i<m;i++)c[i]=0; 39 for(int i=0;i<n;i++)c[x[y[i]]]++; 40 for(int i=1;i<m;i++)c[i]+=c[i-1]; 41 for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; 42 swap(x,y); 43 x[sa[0]]=0,p=1; 44 for(int i=1;i<n;i++) 45 x[sa[i]]= y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++; 46 m=p; 47 if(p>=n)break; 48 } 49 } 50 void getheight(){ 51 int i,j,k=0; 52 for(i=0;i<n;i++)rank[sa[i]]=i; 53 for(i=0;i<n;i++){ 54 if(k)k--; 55 if(rank[i]-1<0)continue; 56 j=sa[rank[i]-1]; 57 while(s[j+k]==s[i+k])k++; 58 height[rank[i]]=k; 59 } 60 } 61 void slove(){ 62 n=strlen(s);n++; 63 getsa(300);getheight(); 64 int ans=0; 65 for(int i=1;i<n;i++) 66 ans+=n-1-sa[i]-height[i]; 67 printf("%d\n",ans); 68 } 69 int main(){ 70 int T; 71 scanf("%d",&T); 72 while(T--){ 73 scanf("%s",s); 74 slove(); 75 } 76 return 0; 77 }
6.[后缀数组]最长回文串
maxcher或者后缀数组;
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 using namespace std; 6 const int maxn=101000; 7 int n=0,p[maxn<<2]; 8 char s[maxn<<2],s1[maxn],s2[maxn]; 9 int main() 10 { 11 //freopen("1.in","r",stdin); 12 scanf("%s",s1); 13 s[n++]=‘$‘; 14 for(int i=0;i<strlen(s1);i++) 15 { 16 s[n++]=‘#‘; 17 s[n++]=s1[i]; 18 } 19 s[n++]=‘#‘; 20 int maxx=0,o=0; 21 for(int mx=0,id=0,i=1;i<n;i++) 22 { 23 if(mx>i)mx=min(p[(id<<1)-i],mx-i); 24 else p[i]=1; 25 while(s[i-p[i]]==s[i+p[i]])p[i]++; 26 if(p[i]+i>mx) 27 { 28 mx=p[i]+i; 29 id=i; 30 } 31 if(p[i]>maxx)maxx=p[i],o=i; 32 } 33 int len=0; 34 for(int i=o;i<maxx+o;i++)if(s[i]!=‘#‘)s2[++len]=s[i]; 35 for(int i=len;i>=1;i--)cout<<s2[i]; 36 for(int i=s[o]==‘#‘?1:2;i<=len;i++)cout<<s2[i]; 37 return 0; 38 }
7.最长公共子串
将两个字符串连起来然后求即可;
1 /*chad*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 #include<ctime> 9 #include<string> 10 using namespace std; 11 const int maxn(200010),inf(1000000000); 12 #define FILE "dealing" 13 #define LL long long 14 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++) 15 namespace IO{ 16 char buf[1<<15],*fs,*ft; 17 int gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} 18 LL read(){ 19 LL x=0,ch=gc(),f=0; 20 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=1;ch=gc();} 21 while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=gc();} 22 return f?-x:x; 23 } 24 }using namespace IO; 25 char s[maxn]; 26 int n,p; 27 int sa[maxn],rank[maxn],t1[maxn],t2[maxn],c[maxn],height[maxn]; 28 void getsa(int m){ 29 int *x=t1,*y=t2; 30 for(int i=0;i<m;i++)c[i]=0; 31 for(int i=0;i<n;i++)c[x[i]=s[i]]++; 32 for(int i=1;i<m;i++)c[i]+=c[i-1]; 33 for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i; 34 for(int k=1,p=0;k<=n;k<<=1){ 35 p=0; 36 for(int i=n-k;i<n;i++)y[p++]=i; 37 for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; 38 for(int i=0;i<m;i++)c[i]=0; 39 for(int i=0;i<n;i++)c[x[y[i]]]++; 40 for(int i=1;i<m;i++)c[i]+=c[i-1]; 41 for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; 42 swap(x,y);x[sa[0]]=0;p=1; 43 for(int i=1;i<n;i++) 44 x[sa[i]]= y[sa[i]]==y[sa[i-1]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++; 45 if(p>=n)break; 46 m=p; 47 } 48 } 49 void getheight(){ 50 int j,k=0; 51 for(int i=0;i<n;i++)rank[sa[i]]=i; 52 for(int i=0;i<n;i++){ 53 if(k)k--; 54 if(rank[i]-1<0)continue; 55 j=sa[rank[i]-1]; 56 while(s[j+k]==s[i+k])k++; 57 height[rank[i]]=k; 58 } 59 } 60 bool check(int mid){ 61 int flag[2]={0,0}; 62 for(int i=1;i<n;i++){ 63 if(flag[0]&&flag[1])return 1; 64 if(height[i]>=mid){ 65 if(sa[i-1]<p)flag[0]=1; 66 else flag[1]=1; 67 if(sa[i]<p)flag[0]=1; 68 else flag[1]=1; 69 } 70 else flag[0]=flag[1]=0; 71 } 72 return 0; 73 } 74 int main(){ 75 scanf("%s",s); 76 n=strlen(s);n++; 77 scanf("%s",s+n); 78 p=strlen(s+n);swap(p,n);n+=p+1;s[n-1]=1; 79 getsa(300),getheight(); 80 int ans=0; 81 for(int i=1;i<n;i++)if((sa[i]>p)==(sa[i-1]<p))ans=max(ans,height[i]); 82 printf("%d\n",ans); 83 return 0; 84 }
后缀数组