重复至少俩次的不重叠子串的个数
我还是觉得大牛的这句解释十分重要,还是没彻底理解透,导致今天这道题目的悲剧
若在假设重复子串的长度最多为L的限制下有解, 则对于任意一个比L小的限制L'<L, 也一定有解. 这就说明存在解的连续性
给出一个关于LCP的定理LCP(SA[i], SA[j]) = RMQ(Height[i+1..j]). 由此, 若存在k, 满足Height[k] < L, 则对于所有i, j 满足i < k < j, 有LCP(SA[i], SA[j]) < L. 即公共长度至少为L的两个后缀, 不会跨过一个小于L的Height低谷k, 所以我们可以得到一些由这些低谷划分开的连续的段.
枚举字串长度h
对于每一次的h,利用height数组,找出连续的height大于等于h的里面最左端和最右端得为之l和r。
如果l +
原来是这样,我一直不能理解,就是为什么是找出连续的height大于等于h,现在稍微有些体会了,在同一连续height大于等于h的区间中,则公共前缀至少为h,这样也不用担心重复计数了,其他长度为h的重复子串则会出现在另一个连续的大于等于h的height
郁闷了那么久…………现在要学会熟练的应用模板啦
这个模板还不错,还挺快的
#include <stdio.h> #include <stdlib.h> #include <string.h> int const N= 1010; int wa[N], wb[N], ws[N], wv[N]; int sa[N], height[N], rank[N],r[N]; char str[N]; int cmp( int* r, int a, int b, int L ){ return r[a]== r[b] && r[a+ L]== r[b+ L]; } void da( 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]]; 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]; t= x, x= y, y= t, p= 1; x[ sa[0] ]= 0; for( i= 1; i< n; ++i ) x[ sa[i] ]= cmp( y, sa[i-1], sa[i], j )? p- 1: p++; } } void callheight( 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++ ); }//上面都是模板来的 int main() { int ans,i,len; while(gets(str)) { if(str[0]=='#') break; len=strlen(str); for(i=0;i<len;i++) r[i]=(int)str[i]; r[len]=0;//这个赋值为0也是关键所在 da(r,sa,len+1,256); callheight(r,sa,len); int ans = 0, minid, maxid; for(i = 1; i <=( len+1)/2; i++)//查一半就好了,长度大于(len+1)/2的子串不可能重复俩次啦 { minid = 1200, maxid = -1; for(int j = 1; j <len+1; j++) { if (height[j] >= i) { if (sa[j - 1] < minid) minid = sa[j - 1]; if (sa[j - 1] > maxid) maxid = sa[j - 1]; if (sa[j] < minid) minid = sa[j]; if (sa[j] > maxid) maxid = sa[j]; } else { if (maxid != -1 && minid + i <= maxid) ans++; minid = 1010, maxid = -1; } } if (maxid != -1 && minid + i <= maxid) ans++;// 这里还要一步判断,因为可能还有一个连续的大于等于h的height还没判断 } printf("%d\n", ans); } return 0; }