一、题目
二、解法
显然 满足单调性,可以二分。
考虑检查,可以
,设
为分段到
,最多能够匹配的长度,转移如下:
其中
是指前缀
最多能够匹配的长度,这个就需要后缀自动机,我们把每一个标准作文串丢进广义后缀自动机中,然后把待检查的作文拿去匹配,如果能够转移就在当前长度的基础上加一,否则跳
树,看祖先能否转移,在第一个能转移的祖先处停止,修改长度,如果没有祖先有转移,就把匹配点赋值为
,长度赋值为
,
就是匹配到
时的长度。
然后在二分里面 ,时间复杂度 。但是这个 其实可以单调队列优化, 每次增加 , 最多每次加 ,所以 单调递增,从 开始 ,每次把 的 值插入单调队列中,然后时间复杂度就可以优化到 。
时间复杂度 ,更多细节可以参考我的代码。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 2000005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,k;char s[M];
struct node
{
int len,fa,ch[2];
node() {memset(ch,0,sizeof ch);len=fa=0;}
};
struct automaton
{
int cnt,last,ans,dp[M],q[M],mt[M];node a[M];
automaton() {cnt=last=1;}
void add(int c)
{
int p=last,np=last=++cnt;
a[np].len=a[p].len+1;
for(;p && !a[p].ch[c];p=a[p].fa) a[p].ch[c]=np;
if(!p) a[np].fa=1;
else
{
int q=a[p].ch[c];
if(a[q].len==a[p].len+1) a[np].fa=q;
else
{
int nq=++cnt;
a[nq]=a[q];a[nq].len=a[p].len+1;
a[q].fa=a[np].fa=nq;
for(;p && a[p].ch[c]==q;p=a[p].fa) a[p].ch[c]=nq;
}
}
}
void match()
{
int p=1,len=0,c=0;
for(int i=1;i<=n;i++)
{
c=s[i]-'0';
if(a[p].ch[c])
p=a[p].ch[c],len++;
else
{
for(;p && !a[p].ch[c];p=a[p].fa);
if(p) len=a[p].len+1,p=a[p].ch[c];
else len=0,p=1;
}
mt[i]=len;
}
}
bool check(int L)
{
for(int i=0;i<L;i++) dp[i]=0;
int head=1,tail=0;
for(int i=L;i<=n;i++)
{
while(head<=tail && dp[q[tail]]-q[tail]<=dp[i-L]-(i-L)) tail--;
q[++tail]=i-L;
while(head<=tail && q[head]<i-mt[i]) head++;
dp[i]=dp[i-1];
if(head<=tail) dp[i]=max(dp[i],dp[q[head]]+i-q[head]);
}
return dp[n]*10>=n*9;
}
void work(int l,int r)
{
if(l>r) return ;
int mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
work(mid+1,r);
}
else work(l,mid-1);
}
void solve()
{
ans=0;
scanf("%s",s+1);
n=strlen(s+1);
match();
work(1,n);
printf("%d\n",ans);
}
}Sam;
int main()
{
m=read();k=read();
for(int i=1;i<=k;i++)
{
Sam.last=1;//
scanf("%s",s);
n=strlen(s);
for(int j=0;j<n;j++)
Sam.add(s[j]-'0');
}
for(int i=1;i<=m;i++)
Sam.solve();
}