题意 :
给出n个数,求n个数组成的串的子串中满足重复k次并且长度最长。
题解:
二分枚举长度,然后判断是存在这样的串,判断方法将后缀分组,然后只要判断是否存在这样的组:里面有至少k个后缀。注意,n个数可以为0,所以全部加1。
#include<iostream> #include<math.h> #include<stdio.h> #include<algorithm> #include<string.h> #include<vector> #include<queue> #include<map> #include<set> using namespace std; #define B(x) (1<<(x)) typedef long long ll; const int oo=0x3f3f3f3f; const ll OO=1LL<<61; const int MOD=10007; const int maxn=1000005; int rank[maxn],SA[maxn],height[maxn]; int t1[maxn],t2[maxn],t3[maxn],cnt[maxn]; int a[maxn],k; void Swap(int*& x,int*& y){ int *temp=x; x=y; y=temp; } int lcp(int t[],int a,int b,int l){ return t[a]==t[b]&&t[a+l]==t[b+l]; } void build_SA(int s[],int len,int up){ int *k1=t1,*k2=t2,*r=t3; for(int i=0;i<up;i++)cnt[i]=0; for(int i=0;i<len;i++)cnt[k1[i]=s[i]]++; for(int i=1;i<up;i++)cnt[i]+=cnt[i-1]; for(int i=len-1;i>=0;i--)SA[--cnt[k1[i]]]=i; for(int d=1,p=0;p<len;d<<=1){ for(int i=len-d;i<len;i++)k2[p++]=i; for(int i=0;i<len;i++)if(SA[i]>=d)k2[p++]=SA[i]-d; for(int i=0;i<len;i++)r[i]=k1[k2[i]]; for(int i=0;i<up;i++)cnt[i]=0; for(int i=0;i<len;i++)cnt[r[i]]++; for(int i=1;i<up;i++)cnt[i]+=cnt[i-1]; for(int i=len-1;i>=0;i--)SA[--cnt[r[i]]]=k2[i]; Swap(k1,k2); k1[SA[0]]=0; p=1; for(int i=1;i<len;i++){ k1[SA[i]]= lcp(k2,SA[i-1],SA[i],d) ? p-1 : p++; } if(p>=len)return; up=p; p=0; } } void get_height(int s[],int len) { for(int i=1;i<=len;i++)rank[SA[i]]=i; for(int i=0,p=0;i<len;i++){ int j=SA[rank[i]-1]; while(s[i+p]==s[j+p])p++; height[rank[i]]=p; if(p)p--; } } bool ok(int tag,int len) { int i=1; int ans=1; for(int i=1;i<=len;i++){ if(height[i]>=tag){ ans++; }else{ ans=1; } if(ans>=k) return true; } return false; } int solve(int len) { int l=1,r=len,mid; int ans=0; while(l<=r){ mid=(l+r)>>1; if(ok(mid,len)){ l=mid+1; ans=mid; }else{ r=mid-1; } } return ans; } int main(){ int n; while(scanf("%d %d",&n,&k)!=EOF){ for(int i=0;i<n;i++)scanf("%d",&a[i]),a[i]++; a[n]=0; build_SA(a,n+1,maxn-1); get_height(a,n); printf("%d\n",solve(n)); } return 0; }