先建出广义后缀自动机。
然后跑出文章中每一个位置的最大匹配距离。
然后定义$f[i]$表示匹配到以$i$结尾的串时,最长的匹配距离。
显然可以二分$L$的取值。
然后容易得到$DP$方程
$f[i]=max(f[i-1],f[j]+i-j)(j<=i-L)$
然后就发现$j$属于一个区间,然后就可以单调队列优化了。
#include <map>
#include <ctime>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)
#define maxn 2200005
namespace SAM{
int last,cnt,go[maxn][2],l[maxn],fa[maxn],n,m,q;
int f[maxn],que[maxn],hd,tl,d[maxn];
char s[maxn];
void init(){last=cnt=1;}
void add(int x)
{
int q,p=last;
if (q=go[p][x])
{
if (l[q]==l[p]+1) last=q;
else
{
int nq=last=++cnt;
l[nq]=l[p]+1;
memcpy(go[nq],go[q],sizeof go[q]);
fa[nq]=fa[q];fa[q]=nq;
for (;go[p][x]==q&&p;p=fa[p]) go[p][x]=nq;
last=nq;
}
}
else
{
int np=last=++cnt; l[np]=l[p]+1;
for (;p&&!go[p][x];p=fa[p]) go[p][x]=np;
if (!p) fa[np]=1;
else
{
q=go[p][x];
if (l[q]==l[p]+1) fa[np]=q;
else
{
int nq=++cnt;l[nq]=l[p]+1;
memcpy(go[nq],go[q],sizeof go[q]);
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for (;p&&go[p][x]==q;p=fa[p]) go[p][x]=nq;
}
}
}
}
void ins()
{
last=1;
scanf("%s",s+1); n=strlen(s+1);
F(i,1,n) add(s[i]-'0');
}
bool check(int mid)
{
f[0]=0; tl=0;hd=1;
F(i,1,n)
{
f[i]=f[i-1];int p=i-mid;
if (p>=0)
{
while (hd<=tl&&f[que[tl]]-que[tl]<f[i-mid]-p)tl--;
que[++tl]=p;
}
while (hd<=tl&&que[hd]<i-d[i]) hd++;
if (hd<=tl) f[i]=max(f[i],f[que[hd]]+i-que[hd]);
}
if (f[n]*10>=n*9) return true;
else return false;
}
void dp()
{
scanf("%s",s+1); n=strlen(s+1);
memset(d,0,(n+1)*sizeof(int));
int now=1,t=0;
F(i,1,n)
{
if (go[now][s[i]-'0'])
now=go[now][s[i]-'0'],t++;
else
{
while (now&&!go[now][s[i]-'0']) now=fa[now];
if (!now) now=1,t=0;
else t=l[now]+1,now=go[now][s[i]-'0'];
}
d[i]=t;
}
int l=0,r=n;
while (l<r)
{
int mid=(l+r)/2+1;
if (check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
void solve()
{
init();
scanf("%d%d",&q,&m);
F(i,1,m) ins();
F(i,1,q) dp();
}
}
int main()
{
using namespace SAM;
solve();
}