正解:SA/二分+哈希
解题报告:
umm像这种子串的问题已经算是比较套路的了,,,?就后缀的公共前缀这样儿的嘛QwQ
所以可以先求个SA
然后现在考虑怎么判断一个长度为d的子串出现了k次?这儿可以直接判断是否有连续的k个hei[i]>=d,因为已经按字典序排序了,所以只要有连续的k个hei[i]>=d就一定有长度大于等于d的子串出现k次
所以二分了check一下就好
然后再说说二分+哈希的做法趴QwQ
就先二分一个长度d,然后把所有长度为d的子串的哈希值求出来,排个序之后判断有没有一个哈希值出现次数>=d
#include<bits/stdc++.h> using namespace std; #define il inline #define gc getchar() #define ll long long #define ri register int #define rb register bool #define rc register char #define rp(i,x,y) for(ri i=x;i<=y;++i) #define my(i,x,y) for(ri i=x;i>=y;--i) const int N=1e6+20,inf=1e9; int x[N],y[N],sa[N],rk[N],hei[N],t[N],n,K,ch[N],l,r; il int read() { rc ch=gc;ri x=0;rb y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=gc; if(ch=='-')ch=gc,y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc; return y?x:x; } il bool check(ri gd,ri gs,ri k){return y[gd]==y[gs] && y[gd+k]==y[gs+k];} il void SA() { ri m=N-10; rp(i,1,n)++t[x[i]=ch[i]]; rp(i,1,m)t[i]+=t[i-1]; my(i,n,1)sa[t[x[i]]--]=i; for(ri k=1;k<=n;k<<=1) { ri p=0; rp(i,0,m)t[i]=0;rp(i,1,n)y[i]=0; rp(i,n-k+1,n)y[++p]=i;rp(i,1,n)if(sa[i]>k)y[++p]=sa[i]-k; rp(i,1,n)++t[x[y[i]]]; rp(i,0,m)t[i]+=t[i-1]; my(i,n,1)sa[t[x[y[i]]]--]=y[i]; swap(x,y); x[sa[1]]=p=1; rp(i,2,n)x[sa[i]]=check(sa[i],sa[i-1],k)?p:++p; if(p>=n)break; m=p; } rp(i,1,n)rk[sa[i]]=i; ri h=0; rp(i,1,n) { if(h)--h; while(ch[i+h]==ch[sa[rk[i]-1]+h])++h; hei[rk[i]]=h;r=max(r,h); } } il bool jud(ri x) { ri cnt=0; rp(i,2,n){if(hei[i]<x)cnt=0;else ++cnt;if(cnt==K-1)return true;}return false; } int main() { // freopen("2852.in","r",stdin);freopen("2852.out","w",stdout); n=read();K=read();rp(i,1,n)ch[i]=read();SA();r=n; while(l<r){ri mid=(l+r)>>1;if(jud(mid+1))l=mid+1;else r=mid;} printf("%d\n",l); return 0; }啊我麻油写二分+哈希,,,就写了个SA的代码QwQ