Milk Patterns 产奶的模式 bzoj-1717 Usaco-2006 Dec
题目大意:给定一个字符串,求最长的至少出现了$k$次的子串长度。
注释:$1\le n\le 2\cdot 10^4$,$2\le k\le n$。
想法:不难想到二分答案,现在我们考虑如何验证。
这里就是后缀数组的一个妙用了。
我们对原串建立后缀数组,观察$ht$数组。
考虑当前二分出来的$mid$。如果有至少连续$k$的$ht$值都不小于$mid$,那么$k$就是合法的。
故此我们直接扫$ht$数组看看最长的连续比$mid$大的$ht$有多少个即可。
Code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1000010
using namespace std;
int wv[N],wa[N],sa[N],Ws[N],wb[N],t[N],rk[N],ht[N],r[N];
int n,m=1000009;
inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}
int rd() {int x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;}
void build_sa()
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<m;i++) Ws[i]=0;
for(i=0;i<n;i++) Ws[x[i]=r[i]]++;
for(i=1;i<m;i++) Ws[i]+=Ws[i-1];
for(i=n-1;~i;i--) sa[--Ws[x[i]]]=i;
for(p=j=1;p<n;j<<=1,m=p)
{
for(p=0,i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]-j>=0) y[p++]=sa[i]-j;
for(i=0;i<n;i++) wv[i]=x[y[i]];
for(i=0;i<m;i++) Ws[i]=0;
for(i=0;i<n;i++) Ws[wv[i]]++;
for(i=1;i<m;i++) Ws[i]+=Ws[i-1];
for(i=n-1;~i;i--) sa[--Ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,i=p=1,x[sa[0]]=0;i<n;i++)
{
if(y[sa[i]]==y[sa[i-1]]&&y[sa[i-1]+j]==y[sa[i]+j]) x[sa[i]]=p-1;
else x[sa[i]]=p++;
}
}
for(i=1;i<n;i++) rk[sa[i]]=i;
for(i=p=0;i<n-1;ht[rk[i++]]=p)
for(p?p--:0,j=sa[rk[i]-1];r[i+p]==r[j+p];p++);
}
template <typename T> void Max(T &x,T y) {x=max(x,y);}
// inline void Max(int &x,int y) {x=max(x,y);}
int check(int x)
{
int re=-1,now=0;
for(int i=0;i<n;i++)
{
if(ht[i]>=x) now++;
else Max(re,now),now=1;
}
Max(re,now);
return re;
}
int main()
{
n=rd();int k=rd(); for(int i=0;i<n;i++) r[i]=rd();
r[n++]=0; build_sa();
int l=0,r=n+1;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)>=k) l=mid+1;
else r=mid;
}
l--;
cout << l << endl ;
return 0;
}
小结:后缀数组有很多特别有趣的妙用,要多积累啊!因为开$iostream$的缘故所以不能有变量叫做$ws$,我就叫$Ws$了……