最近看的后缀数组,做了几个应用。
1、poj 2774 求两个字符串的最长公共子串。
思路:将第二个串接到第一个串的后面,第一个串的结尾和第二个串的结尾分别用一个不会出现的字符标记,第二个串的结尾标记值要小。
用大牛的模板求出sa,height,rank数组。然后去遍历数组求前面的和后面的最大公共前缀。判定的时候注意判定条件,我在判定的时候判定
条件有漏洞,然后wa了好多次~~
#include <cstdio> #include <cstdlib> #include <climits> #include <cmath> #include <cstring> #include <iostream> #include <algorithm> #include <queue> #include <stack> #include <map> using namespace std; typedef long long ll; #define mid(x,y) (x+y)>>1 #define INF (INT_MAX/10) #define SQR(x) x*x #define rep(i, n) for(int i=0;i<n;i++) #define repf(i, a, b) for(int i=a;i<=b;i++) #define repd(i, a, b) for(int i=a;i>=b;i--) #define clr(ar, val) memset(ar, val, sizeof(ar)) #define N 1000000 int sa[N+10],c[N+10],wa[N+10],wb[N+10]; int cmp(int *y,int a,int b,int l){ return y[a]==y[b]&&y[a+l]==y[b+l]; } void da(string r,int *sa){ int i,j,p,*rand=wa,*y=wb,m=255,n=r.length(); for(i=0;i<=m;i++) c[i]=0; for(i=0;i<n;i++) c[rand[i]=r[i]]++; for(i=0;i<m;i++) c[i+1]+=c[i]; for(i=n-1;i>=0;i--) sa[--c[rand[i]]]=i; for(j=1,p=1;p<n;j*=2,m=p){ for(i=n-j,p=0;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<=m;i++) c[i]=0; for(i=0;i<n;i++) c[rand[y[i]]]++; for(i=0;i<m;i++) c[i+1]+=c[i]; for(i=n-1;i>=0;i--) sa[--c[rand[y[i]]]]=y[i]; swap(y,rand); for(i=1,p=1,rand[sa[0]]=0;i<n;i++){ rand[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } } } void height(string r,int *sa,int *h){ int i,j,*rank=wa,k=0,n=r.length(); for(i=0;i<n;i++) rank[sa[i]]=i; for(i=0;i<n-1;h[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); } string str1,str2; int ans[N+10],h[N+10]; int main(){ int k,maxs; while(cin>>str1>>str2){ clr(ans,0);clr(h,0); str1=str1+'$'+str2+'#'; //cout << str1 << endl; k=str1.find('$'); //cout << k << ' ' << str1.length() << endl; da(str1,ans); //rep(i,10) cout << ans[i] << ' ' ; //cout << endl; height(str1,ans,h); //rep(i,10) cout << h[i] << ' ' ; //cout << endl; maxs=0; repf(i,1,(int)str1.length()-1) if((ans[i]-k)*(ans[i-1]-k+h[i])<0||(ans[i]-k+h[i])*(ans[i-1]-k)<0) maxs=max(maxs,h[i]); cout << maxs << endl; } return 0; }
2、poj 1743 求一个字符串中重复出现的最长的子串(不可重叠)
思路:数据要先预处理成一组新的字符串,即后面的减掉前面的。对新串求sa,height,rank数组,然后二分的去搜可能的值,我当时没有明白二分的意义,
后来看了别人的代码,再对着文字的理论结合大概理解了。找一个可能的长度,带到字符串中,判定是否可行,可行的话看是否有更长的,不可行看是不
是有更短的。这样二分的找出结果。
#include <cstdio> #include <cstdlib> #include <climits> #include <cmath> #include <cstring> #include <iostream> #include <algorithm> #include <queue> #include <stack> #include <map> using namespace std; typedef long long ll; #define Mid(x,y) (x+y)>>1 #define INF (INT_MAX/10) #define SQR(x) x*x #define rep(i, n) for(int i=0;i<n;i++) #define repf(i, a, b) for(int i=a;i<=b;i++) #define repd(i, a, b) for(int i=a;i>=b;i--) #define clr(ar, val) memset(ar, val, sizeof(ar)) #define N 1000000 int sa[N+10],c[N+10],wa[N+10],wb[N+10]; bool cmp(int *y,int a,int b,int l){ return y[a]==y[b]&&y[a+l]==y[b+l]; } template <class T> void suffix(T r,int n){ int i,j,p,*rank=wa,*y=wb,m=255; for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[rank[i]=r[i]]++; for(i=0;i<m;i++) c[i+1]+=c[i]; for(i=n-1;i>=0;i--) sa[--c[rank[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<m;i++) c[i]=0; for(i=0;i<n;i++) c[rank[y[i]]]++; for(i=0;i<m;i++) c[i+1]+=c[i]; for(i=n-1;i>=0;i--) sa[--c[rank[y[i]]]]=y[i]; swap(rank,y); for(i=1,p=1,rank[sa[0]]=0;i<n;i++) rank[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++; } } int h[N+10]; template <class T> void height(T r,int n){ int i,j,k=0,*rank=wa; for(i=0;i<n;i++) rank[sa[i]]=i; for(i=0;i<n-1;h[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); } int check(int mid,int len){ for(int i=2;i<len;i++){ if(h[i]<mid) continue; for(int j=i-1;j>=2;j--){ if(abs(sa[i]-sa[j])>mid) return 1; if(h[j]<mid) break; } } return 0; } int str[N+10]; int main(){ int n,ans; while(1){ cin >> n; if(!n) break; rep(i,n) { scanf("%d",str+i); if(i) str[i-1]=str[i]-str[i-1]+100; } if(n<10){ cout << 0 << endl; continue; } str[n-1]=0; // rep(i,n) cout << str[i] << ' ' ; // cout << endl; suffix(str,n); height(str,n); // rep(i, n) cout << sa[i] << ' ' ; // cout << endl; // rep(i, n) cout << h[i] << ' ' ; // cout << endl; int mid,left,right; left=3,right=n; ans=0; while(right>=left){ mid=Mid(left,right); // cout << mid << endl; if(check(mid,n)){ left=mid+1; ans=mid; } else right=mid-1; } // cout << ans << endl; if(ans<4) cout << 0 << endl; else cout << ans+1 << endl; } return 0; }3、poj 3261 同样的求最长的重复子串
题意很明确,求一个最长的出现K 次的重复子串,可以重叠。同样处理出sa,height,rank数组,再二分的搜索可能的长度。这个题我wa了几次,
wa的有点坑,一个等号的问题(<=len)!!!!eggache~~
#include <cstdio> #include <cstdlib> #include <climits> #include <cmath> #include <cstring> #include <iostream> #include <algorithm> #include <queue> #include <stack> #include <map> using namespace std; typedef long long ll; #define Mid(x,y) (x+y)>>1 #define INF (INT_MAX/10) #define SQR(x) x*x #define rep(i, n) for(int i=0;i<n;i++) #define repf(i, a, b) for(int i=a;i<=b;i++) #define repd(i, a, b) for(int i=a;i>=b;i--) #define clr(ar, val) memset(ar, val, sizeof(ar)) #define N 1000000 int sa[N+10],c[N+10],wa[N+10],wb[N+10]; bool cmp(int *y,int a,int b,int l){ return y[a]==y[b]&&y[a+l]==y[b+l]; } template <class T> void suffix(T r,int n,int m){ int i,j,p,*rank=wa,*y=wb; for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[rank[i]=r[i]]++; for(i=0;i<m;i++) c[i+1]+=c[i]; for(i=n-1;i>=0;i--) sa[--c[rank[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<m;i++) c[i]=0; for(i=0;i<n;i++) c[rank[y[i]]]++; for(i=0;i<m;i++) c[i+1]+=c[i]; for(i=n-1;i>=0;i--) sa[--c[rank[y[i]]]]=y[i]; swap(rank,y); for(i=1,p=1,rank[sa[0]]=0;i<n;i++) rank[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++; } } int h[N+10]; template <class T> void height(T r,int n){ int i,j,k=0,*rank=wa; for(i=0;i<n;i++) rank[sa[i]]=i; for(i=0;i<n-1;h[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); } int check(int mid,int len,int k){ int i,j=1; for(i=2;i<=len;i+=j){ // cout << mid << ' ' << k << endl; if(h[i]<mid) continue; int flag=0; while(i<=len&&h[i]>=mid) flag++,i++; if(flag>=k) return 1; } return 0; } int str[N+10]; int main(){ int n,k; while(cin>>n>>k){ rep(i,n) { scanf("%d",str+i); str[i]++; } str[n]=0; suffix(str,n+1,N+3); height(str,n+1); // rep(i, n) cout << h[i] << ' ' ; // cout << endl; int ans=0,mid,left,right; left=0,right=n;//=*max_element(h+2,h+n); // cout << "fuck" << right << endl; while(right>=left){ mid=Mid(left,right); // cout << "fuck mid " << mid << endl; if(check(mid,n,k-1)) left=mid+1,ans=mid; else right=mid-1; } cout << ans << endl; } return 0; }