秒掉2/8的男人...
这题也是考察的后缀数组基础应用:可重叠至少重复k次的最长子串
显然还是对height数组应用于二分答案的检验
二分一个长度,然后用height数组检验即可
注意:不能单纯看height数组中出现某个值的次数,而是要关注height数组中连续出现某个值的次数,否则无法判断这个公共前缀是否相同
代码:
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> using namespace std; int sa[20005]; int rank[20005]; int height[20005]; int s[20005]; int a[20005]; int f1[20005]; int f2[20005]; int f3[20005]; int to[1000005]; int has[20010]; int l,k,m=20003; void turnit() { memcpy(f3,f1,sizeof(f3)); memcpy(f1,f2,sizeof(f1)); memcpy(f2,f3,sizeof(f2)); } void get_sa() { for(int i=1;i<=l;i++) { f1[i]=s[i]; has[s[i]]++; } for(int i=2;i<=m;i++) { has[i]+=has[i-1]; } for(int i=l;i>=1;i--) { sa[has[f1[i]]--]=i; } for(int k=1;k<=l;k<<=1) { int cnt=0; for(int i=l-k+1;i<=l;i++) { f2[++cnt]=i; } for(int i=1;i<=l;i++) { if(sa[i]>k) { f2[++cnt]=sa[i]-k; } } for(int i=1;i<=m;i++) { has[i]=0; } for(int i=1;i<=l;i++) { has[f1[i]]++; } for(int i=2;i<=m;i++) { has[i]+=has[i-1]; } for(int i=l;i>=1;i--) { sa[has[f1[f2[i]]]--]=f2[i]; f2[i]=0; } turnit(); f1[sa[1]]=1; cnt=1; for(int i=2;i<=l;i++) { if(f2[sa[i]]==f2[sa[i-1]]&&f2[sa[i]+k]==f2[sa[i-1]+k]) { f1[sa[i]]=cnt; }else { f1[sa[i]]=++cnt; } } if(cnt==l) { break; } m=cnt; } for(int i=1;i<=l;i++) { rank[sa[i]]=i; } int f=0; for(int i=1;i<=l;i++) { if(rank[i]==1) { continue; } if(f) { f--; } int j=sa[rank[i]-1]; while(s[i+f]==s[j+f]) { f++; } height[rank[i]]=f; } } bool check(int v) { int cnt=0; for(int i=2;i<=l;i++) { if(height[i]>=v) { cnt++; if(cnt>=k-1) { return 1; } }else { cnt=0; } } return 0; } int divi() { int lq=0,rq=l; int ans=0; while(lq<=rq) { int mid=(lq+rq)>>1; if(check(mid)) { ans=mid; lq=mid+1; }else { rq=mid-1; } } return ans; } int main() { scanf("%d%d",&l,&k); for(int i=1;i<=l;i++) { scanf("%d",&s[i]); a[i]=s[i]; } sort(a+1,a+l+1); int tot=1; for(int i=1;i<=l;i++) { if(a[i]==a[i-1]) { to[a[i]]=tot; }else { to[a[i]]=++tot; } } for(int i=1;i<=l;i++) { s[i]=to[s[i]]; } get_sa(); printf("%d ",divi()); return 0; }