1、后缀数组题型讲解后缀数组、不重复子串 Distinct Substrings题目大意:给出一个字符串,问它的不重复子串有多少个。两题是一样的,除了字符串长度.分析:用后缀数组可以轻松解决。因为这个字符串的每个子串必然是某个后缀的前缀,先用后缀数组求出sa和height,那么对于sak,它有n-sak个子串,其中有heightk个是和上一个后缀重复的,所以要减去。所以用后缀数组求解的时间复杂度是O(n),后缀数组要是用倍增算法是O(nlog2n),效率很高。note:wa了一次,主要原因是忘了an=0这个关键的初值.PS:各位大牛对我的差劲的c+代码有什么看法可以尽管喷哈!codes:#incl
2、ude#includeusing namespace std;const long maxn=1010;long wnmaxn,wamaxn,wbmaxn,wvmaxn,amaxn,samaxn,rankmaxn,heightmaxn;char rmaxn;long cmp(long *r,long a,long b,long l) return ra=rb&ra+l=rb+l;void da(long *r,long *sa,long n,long m) long i,j,p,*x=wa,*y=wb,*t; for (i=0;im;i+) wni=0; for (i=0;in;i+) wnx
3、i=ri+; for (i=1;i=0;i-) sa-wnxi=i; for (p=1,j=1;pn;j*=2,m=p) for (p=0,i=n-j;in;i+) yp+=i; for (i=0;i=j) yp+=sai-j; for (i=0;im;i+) wni=0; for (i=0;in;i+) wnwvi=xyi+; for (i=1;i=0;i-) sa-wnwvi=yi; for (t=x,x=y,y=t,xsa0=0,p=1,i=1;in;i+) xsai=cmp(y,sai-1,sai,j)?p-1:p+; return;void calheight(long *r,lon
4、g *sa,long n) long i,j,k=0; for (i=1;i=n;i+) ranksai=i;heighti=0; for (i=0;i t; while (t-) cin r; long n=strlen(r); for (int i=0;in;i+) ai=static_cast(ri); an=0; da(a,sa,n+1,256); calheight(a,sa,n); long sum=0; for (i=1;i=n;i+) sum+=n-sai-heighti; cout sum =0;i-)以及cmp(y,sai-1,sai,j)。2、w数组理解错误,开小了- -
5、实际应该开maxn。结果re了一次.#include#includeusing namespace std;const int maxn=100010;int wmaxn,wamaxn,wbmaxn,wvmaxn,amaxn,samaxn,rankmaxn,heightmaxn;int cmp(int *r,int a,int b,int l) return ra=rb&ra+l=rb+l;void da(int *r,int *sa,int n,int m) int i,j,p,*x=wa,*y=wb,*t; for (i=0;im;i+) wi=0; for (i=0;in;i+) wxi
6、=ri+; for (i=1;i=0;i-) sa-wxi=i; for (p=1,j=1;pn;j*=2,m=p) for (p=0,i=n-j;in;i+) yp+=i; for (i=0;i=j) yp+=sai-j; for (i=0;im;i+) wi=0; for (i=0;in;i+) wwvi=xyi+; for (i=1;i=0;i-) sa-wwvi=yi; for (t=x,x=y,y=t,p=1,i=1,xsa0=0;in;i+) xsai=cmp(y,sai-1,sai,j)?p-1:p+; return;void cal(int *r,int *sa,int n)
7、int i,j,k=0; for (i=1;i=n;i+) ranksai=i; for (i=0;i t; while (t-) char smaxn; cin s; int n=strlen(s); for (i=0;in;i+) ai=static_cast(si); an=0; da(a,sa,n+1,255); cal(a,sa,n); int max=0,k=0; for (i=1;i=n;i+) if (maxheighti) max=heighti; k=sai; for (i=k,j=0;jmax;j+,i+) cout si; cout 0,而直接做差会使数组中的元素变成负
8、数,所以可以在做差后加上一个足够大的常数,来避免负数的出现。2、二分答案要写好很不容易。我现在还有点糊涂- -3、输入用scanf。题目里也有这个提示.codes:#includeusing namespace std;const int maxn=20010;int wmaxn,wamaxn,wbmaxn,wvmaxn,amaxn,xmaxn,samaxn,rankmaxn,heightmaxn,n;int cmp(int *r,int a,int b,int l) return ra=rb&ra+l=rb+l;void da(int *r,int *sa,int n,int m) int
9、i,j,p,*x=wa,*y=wb,*t; for (i=0;im;i+) wi=0; for (i=0;in;i+) wxi=ri+; for (i=1;i=0;i-) sa-wxi=i; for (p=1,j=1;pn;j*=2,m=p) for (p=0,i=n-j;in;i+) yp+=i; for (i=0;i=j) yp+=sai-j; for (i=0;im;i+) wi=0; for (i=0;in;i+) wwvi=xyi+; for (i=1;i=0;i-) sa-wwvi=yi; for (t=x,x=y,y=t,p=1,i=1,xsa0=0;in;i+) xsai=cm
10、p(y,sai-1,sai,j)?p-1:p+; return;void cal(int *r,int *sa,int n) int i,j,k=0; for (i=1;i=n;i+) ranksai=i; for (i=0;in;heightranki+=k) for (k?k-:0,j=saranki-1;ri+k=rj+k;k+); return;int check(int k) int max=0,min=maxn; for (int i=1;i=n;i+) if (heightisai?max:sai; min=min=k) return 1; if (max-min=k) retu
11、rn 1; else return 0;int Bin_search(int l,int r) int mid; for (mid=(l+r)1;l1) if (check(mid) l=mid+1; else r=mid-1; if (check(mid) return mid; else return 0; int main() scanf(%d,&n); while (n) int i,k; for (i=0;in;i+) scanf(%d,&xi); a0=500; for (i=1;in;i+) ai=xi-xi-1+100; an=0; da(a,sa,n+1,501); cal(
12、a,sa,n); k=Bin_search(0,n/2)+1; if (k5) k=0; printf(%dn,k); scanf(%d,&n); return 0; -后缀数组、出现k次的重复子串题目大意:给出n个数字组成的一个字符串,求最长的恰好出现k次的重复子串(可重叠)的字符串的长度。分析:后缀数组一个经典的应用。先二分答案,然后分组。只要某一组包含的后缀数量大于等于k,表示有解。这个不难理解。等完成了论文里面的练习之后,我再写个总结笔记吧。深刻体会到后缀数组的强大.note:1、分组时,每次heightik时,第i个后缀是归到下一个组中的。 2、scanf()返回的值是成功读取了多少
13、个数据,如果文件结束了,返回EOF。一开始不知道这个,结果OLE= =codes:#includeusing namespace std;const int maxn=20010;int wmaxn*2,wamaxn,wbmaxn,wvmaxn,samaxn,rankmaxn,heightmaxn,amaxn;int n,m,k;int cmp(int *r,int a,int b,int l) return ra=rb&ra+l=rb+l;void da(int *r,int *sa,int n,int m) int i,j,p,*x=wa,*y=wb,*t; for (i=0;im;i+)
14、 wi=0; for (i=0;in;i+) wxi=ri+; for (i=1;i=0;i-) sa-wxi=i; for (p=1,j=1;pn;j*=2,m=p) for (i=n-j,p=0;in;i+) yp+=i; for (i=0;i=j) yp+=sai-j; for (i=0;im;i+) wi=0; for (i=0;in;i+) wwvi=xyi+; for (i=1;i=0;i-) sa-wwvi=yi; for (t=x,x=y,y=t,xsa0=0,p=1,i=1;in;i+) xsai=cmp(y,sai-1,sai,j)?p-1:p+; return; void
15、 cal(int *r,int *sa,int n) int i,j,k=0; for (i=1;i=n;i+) ranksai=i; for (i=0;in;heightranki+=k) for (k?k-:0,j=saranki-1;ri+k=rj+k;k+); return; int check(int x) int i,cnt; for (i=1;i=n;i+) if (heighti=k) return 1; if (cnt=k) return 1; return 0; int bin_search(int l,int r) int mid; for (mid=(r+l)1;l1)
16、 if (check(mid) l=mid+1; else r=mid-1; return mid;int main() int i; while (scanf(%d %d,&n,&k)!=EOF) m=0; for (i=0;iai?m:ai; an=0; m+; da(a,sa,n+1,m); cal(a,sa,n); int k=bin_search(0,n); printf(%dn,k); return 0; -后缀数组、最长回文子串题目大意:给出一个字符串,求它的最长回文子串。分析:这题数据规模不大(n=1000),所以直接暴力可以解决。不过如果数据规模大了,暴力就不行了。这里介绍后
17、缀数组的做法。首先,枚举回文子串的中心所在位置。这里要分回文串长度为奇数和偶数两种情况考虑。这两个问题均可以转化为求一个后缀和一个倒着写的后缀的最长公共前缀。具体地,将原串与反着写之后的原串相连,中间以一个特殊字符隔开。这个特殊字符只要不是0号,不影响后缀的排序,就没有问题。(不能是0是因为我的倍增算法要求除了字符串的最后一位以外,其它位不能为0,否则会出错)然后算出height数组。两个后缀的最长公共前缀为两个后缀排序之后,它们之间的串的height值的最小值。这个可以自己举个具体例子好好体会。用st算法求解rmq问题即可。notes:1、st算法我写得实在是少- -for循环的先后要注意,
18、先循环j,再循环i。 2、我在写的时候,加了一个错误的判断条件,结果导致wa on test 22,以后加啥代码都要有严谨的逻辑才好加.codes:#include#include#includeusing namespace std;const int maxn=2010;int n,wmaxn,wamaxn,wbmaxn,wvmaxn,amaxn,samaxn,rankmaxn,heightmaxn,fmaxn20;int cmp(int *r,int a,int b,int l) return ra=rb&ra+l=rb+l;void da(int *r,int *sa,int n,in
19、t m) int i,j,p,*x=wa,*y=wb,*t; for (i=0;im;i+) wi=0; for (i=0;in;i+) wxi=ri+; for (i=1;i=0;i-) sa-wxi=i; for (p=1,j=1;pn;m=p) for (p=0,i=n-j;in;i+) yp+=i; for (i=0;i=j) yp+=sai-j; for (i=0;im;i+) wi=0; for (i=0;in;i+) wwvi=xyi+; for (i=1;i=0;i-) sa-wwvi=yi; for (t=x,x=y,y=t,p=1,xsa0=0,i=1;in;i+) xsa
20、i=cmp(y,sai-1,sai,j)?p-1:p+; return;void cal(int *r,int *sa,int n) int i,j,k=0; for (i=1;i=n;i+) ranksai=i; for (i=0;ib) return a; else return b;int nmin(int a,int b) if (ab) return a; else return b;void rmq(int *rank,int n) int i,j; memset(f,127,sizeof(f); for (i=1;i=n;i+) fi0=heighti; for (j=1;j20;j+) for (i=1;i+(1j)-1=n;i+) fij=nmin(fij-1,f(1b) int t=a; a=b; b=t; a+; int t=int(log(double(b-a+1)/log(2.00); return nmin(fat,fb-(1t)+1t);int main() char smaxn; cin
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1