注意:对整型数组求sa时,s[n]请置成-1。
请离散化。
可重叠的 k 次最长重复子串(pku3261)
给定一个字符串,求至少出现 k 次的最长重复子串,这 k 个子串可以重叠。
算法分析:
先二分答案,然后将后缀分成若干组。 不
同的是,这里要判断的是有没有一个组的后缀个数不小于 k。如果有,那么存在
k 个相同的子串满足条件,否则不存在。这个做法的时间复杂度为 O(nlogn)。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define N 20001 struct Point{int p,v;}T[N]; bool operator < (Point a,Point b){return a.v<b.v;} int s[N],tong[N],t[N],t2[N],rank[N],lcp[N],sa[N]; int n,K,zy=1,ma[N]; bool cmp(int *y,int i,int k) { return ((y[sa[i-1]]==y[sa[i]])&&((sa[i-1]+k>=n?-1:y[sa[i-1]+k])==(sa[i]+k>=n?-1:y[sa[i]+k]))); } void build_sa(int range) { int *x=t,*y=t2; memset(tong,0,sizeof(int)*range); for(int i=0;i<n;++i) tong[x[i]=s[i]]++; for(int i=1;i<range;++i) tong[i]+=tong[i-1]; for(int i=n-1;i>=0;--i) sa[--tong[x[i]]]=i; for(int k=1;k<=n;k<<=1) { int p=0; for(int i=n-k;i<n;++i) y[p++]=i; for(int i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k; memset(tong,0,sizeof(int)*range); for(int i=0;i<n;++i) tong[x[y[i]]]++; for(int i=1;i<range;++i) tong[i]+=tong[i-1]; for(int i=n-1;i>=0;--i) sa[--tong[x[y[i]]]]=y[i]; swap(x,y); p=1; x[sa[0]]=0; for(int i=1;i<n;++i) x[sa[i]]=cmp(y,i,k)?p-1:p++; if(p>=n) break; range=p; } } void get_lcp() { int k=0; for(int i=0;i<n;++i) rank[sa[i]]=i; for(int i=0;i<n;++i) if(rank[i]) { if(k) --k; int j=sa[rank[i]-1]; while(s[i+k]==s[j+k]) ++k; lcp[rank[i]]=k; } } bool check(int x) { int cnt=1; for(int i=1;i<=n;++i) { if(lcp[i]<x||i==n) { if(cnt>=K) return 1; cnt=1; } else if(lcp[i]>=x) ++cnt; } return 0; } int main() { scanf("%d%d",&n,&K); for(int i=0;i<n;++i) { scanf("%d",&T[i].v); T[i].p=i; } sort(T,T+n); for(int i=1;i<n;++i) { if(T[i].v!=T[i-1].v) ++zy; s[T[i].p]=zy-1; } s[n]=-1; build_sa(zy); get_lcp(); int l=0,r=n; while(r>l) { int mid=(l+r+1>>1); if(check(mid)) l=mid; else r=mid-1; } printf("%d ",l); return 0; }