后缀数组欧液!
大意就是给出一串数字 (1-88)
要寻找重复的最长不重叠公共子串儿 串长至少5
but 由题意 12345 和 23456 可以算是重复的公共子串 因为相当于每个数加上了相同的数
就是保持 差的顺序 不变啦
就转化成求 差的序列中 最长至少为4的不重叠公共子串儿
于是 字符串里放的是差值 但是由于前后差可能为负数 而每个字符串会作为基数排序中的下标 故要 加一个数使该数为正数
然后二份查找 下限可以从长度4开始
还要记录满足长度条件的间隔最大的下标 若〉4则满足条件
该题 trick多多
n==1的时候 输出为0
#include<iostream> #include<string.h> #include<cstdio> #define MIN(a,b) (a)>(b)?(b):(a) #define MAX(a,b) (a)>(b)?(a):(b) #define INF 0x3f3f3f using namespace std; const int M=20010; int N; int str[M]; int sa[M] , rank[M] , height[M]; int wa[M] , wb[M] , wv[M] , wd[M]; 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 n , int m) //n为字符串长度,m为字符的取值范围,r为字符串。后面的j为每次排序时子串的长度 { int i , j , p , *x = wa , *y = wb , *t; for (i = 0 ; i < m ; i++)wd[i] = 0; for (i = 0 ; i < n ; i++)wd[x[i]=r[i]]++; for (i = 1 ; i < m ; i++)wd[i] += wd[i-1]; for (i = n-1 ; i >= 0 ; i--) sa[--wd[x[i]]] = i; for (j = 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++)wd[i] = 0; for (i = 0 ; i < n ; i++)wd[wv[i]]++; for (i = 1 ; i < m ; i++)wd[i] += wd[i-1]; for (i = n-1 ; i >= 0 ; i--)sa[--wd[wv[i]]] = y[i]; 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++; } } /*height数组的值应该是从height[1]开始的,而且height[1]应该是等于0的。 原因是,+因为我们在字符串后面添加了一个0号字符(不好意思该题我没加),所以它必然是最小的 一个后缀。</span><span style="margin:0px; padding:0px; line-height:1.5"><span style="color:#ff0000">而字符串中的其他字符都应该是大于0的</span></span><span style="margin:0px; padding:0px; line-height:1.5"><span style="color:#ff0000">(前面有提到,使用倍 增算法前需要确保这点</span></span><span style="margin:0px; padding:0px; line-height:1.5; color:rgb(0,128,0)">),所以排名第二的字符串和0号字符的公共前缀 (即height[1])应当为0.在调用calheight函数时,要注意height数组的范 围应该是[1..n]。所以调用时应该是calheight(r,n) (所以我这题是 N-1)*/ void calheight(int *r , 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 find(int n) { int l=4,r=n;//长度至少为4 二分找最大长度 int ans=0; while(l<=r) { int mid=(l+r)>>1; bool flag=0; int num=0; int head=INF,next=0;//标记满足条件的后缀的间隔最长前缀开头下标 for(int i=1;i<=n;i++){ if(height[i]>=mid) { num++; head=MIN(head,MIN(sa[i-1],sa[i])); next=MAX(next,MAX(sa[i-1],sa[i])); } else{ if(num+1>=2){ if(next-head>mid) //若满足间隔大于重复的长度 注意是大于不能等于 .等于的话可以参考输入 9个1时的例子 { ans=mid; flag=1; } } num=0; } } if(num+1>=2){ if(next-head>mid) { ans=mid; flag=1; } } if(flag) l=mid+1; else r=mid-1; } return ans;//返回最长不重复差子串长度 若小于4即为0 } int main() { //freopen("test.txt", "r", stdin); while(scanf("%d",&N)!=EOF,N) { int i; int a,b; scanf("%d",&a); if(N==1) { printf("0\n"); continue; } for(i=0;i<N-1;i++) { scanf("%d",&b); str[i]=b-a+100; a=b; } da(str,N,200); calheight(str,N-1); int tp=find(N-1); if(tp) printf("%d\n",tp+1);//因为是差值 所以要加上1才为真正的子串长 else printf("0\n"); } return 0; }