首先我们建出广义SAM,然后对于每一个询问串S,我们处理出len[i],表示最大的S[i-len[i]+1…i]可以被匹配。我们二分答案,dp判定,f[i]表示前i个字符最多匹配多少位。
f[i]=max{f[i−1],f[j]+i−j|i−len[i]<=j<=i−L}
j的取值范围是随着i的增大而单调右移的,因此可以单调队列优化到O(n)
总的复杂度O(nlogn)
注意不要随便清空大数组qaq,这样会使你的复杂度炸掉的!
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define N 1000010
#define ll long long
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n=0,m,last,rt,par[N<<1],mx[N<<1],son[N<<1][2],len[N],f[N],qq[N];
char s[N];
inline void ins(int ch){
int p=last;
if(son[p][ch]){
int q=son[p][ch];
if(mx[q]==mx[p]+1){last=q;return;}
int nq=++n;mx[nq]=mx[p]+1;last=nq;par[nq]=par[q];par[q]=nq;
memcpy(son[nq],son[q],sizeof(son[q]));
for(;p&&son[p][ch]==q;p=par[p]) son[p][ch]=nq;return;
}int np=++n;mx[np]=mx[p]+1;last=np;
for(;p&&!son[p][ch];p=par[p]) son[p][ch]=np;
if(!p){par[np]=rt;return;}
int q=son[p][ch];
if(mx[q]==mx[p]+1){par[np]=q;return;}
int nq=++n;mx[nq]=mx[p]+1;memcpy(son[nq],son[q],sizeof(son[q]));
par[nq]=par[q];par[q]=par[np]=nq;
for(;p&&son[p][ch]==q;p=par[p]) son[p][ch]=nq;
}
inline int gao(){
int p=rt,sum=0,res=0;
for(int i=1;i<=m;++i){
int ch=s[i]-'0';
if(son[p][ch]) p=son[p][ch],++sum;
else{
while(p&&!son[p][ch]) p=par[p];
if(!p) p=rt,sum=0;
else sum=mx[p]+1,p=son[p][ch];
}len[i]=sum;res=max(res,len[i]);
}return res;
}
inline bool jud(int L){
int qh=1,qt=0;f[0]=0;
for(int i=1;i<=m;++i){
int j=i-L;
if(j>=0){
while(qh<=qt&&f[qq[qt]]-qq[qt]<=f[j]-(j)) --qt;
qq[++qt]=j;
}while(qh<=qt&&qq[qh]<i-len[i]) ++qh;f[i]=f[i-1];
if(qh<=qt) f[i]=max(f[i],f[qq[qh]]-qq[qh]+i);
}return f[m]*10>=m*9;
}
int main(){
// freopen("a.in","r",stdin);
int n1=read(),n2=read();last=rt=++n;
while(n2--){
scanf("%s",s+1);m=strlen(s+1);last=rt;
for(int i=1;i<=m;++i) ins(s[i]-'0');
}while(n1--){
scanf("%s",s+1);m=strlen(s+1);
int l=1,r=gao();
while(l<=r){
int mid=l+r>>1;
if(jud(mid)) l=mid+1;
else r=mid-1;
}printf("%d\n",l-1);
}return 0;
}