建出模板串的广义后缀自动机,就可以方便地对询问串计算出 fi 表示以 i 结尾的子串最多能匹配多长。二分答案
dpi=max{
dpi?1,dpj+i?j(x≤i?j≤fi)}
后面那个限制条件变形为 i?fi≤j≤i?x 。 i?x 显然单调,而容易发现 fi+1≤fi+1 ,因此 i?fi 也单调。于是我们可以用单调队列优化dp,判断就很简单了。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=3500000;
const double eps=1e-10;
char s[maxn];
int trie[maxn][2],trans[maxn][2],fail[maxn],val[maxn],
id[maxn],que[maxn],dp[maxn],f[maxn],
q,n,m,num,tot=1;
int add(int u,int x)
{int nu=++tot,v,nv;val[nu]=val[u]+1;while (u&&!trans[u][x]){trans[u][x]=nu;u=fail[u];}if (!u) fail[nu]=1;else{v=trans[u][x];if (val[v]==val[u]+1) fail[nu]=v;else{val[nv=++tot]=val[u]+1;fail[nv]=fail[v];fail[v]=fail[nu]=nv;trans[nv][0]=trans[v][0];trans[nv][1]=trans[v][1];while (u&&trans[u][x]==v){trans[u][x]=nv;u=fail[u];}}}return nu;
}
int main()
{int u,hd,tl,now,x,l,r,mid;scanf("%d%d",&q,&m);while (m--){scanf("%s",s+1);n=strlen(s+1);u=0;for (int i=1;i<=n;i++){if (!trie[u][s[i]-'0']) trie[u][s[i]-'0']=++num;u=trie[u][s[i]-'0'];}}id[0]=1;que[hd=tl=1]=0;while (hd<=tl){u=que[hd++];if (trie[u][0]){id[trie[u][0]]=add(id[u],0);que[++tl]=trie[u][0];}if (trie[u][1]){id[trie[u][1]]=add(id[u],1);que[++tl]=trie[u][1];}}while (q--){scanf("%s",s+1);n=strlen(s+1);u=1;now=0;for (int i=1;i<=n;i++){x=s[i]-'0';if (!trans[u][x]){while (u&&!trans[u][x]) u=fail[u];now=val[u];}if (!u){u=1;f[i]=now=0;}else{u=trans[u][x];f[i]=++now;}}l=0;r=n;while (l<r){mid=(l+r+1)/2;hd=1,tl=0;for (int i=1;i<=n;i++){if (i-mid>=0){while (hd<=tl&&dp[que[tl]]-que[tl]<=dp[i-mid]-i+mid) tl--;que[++tl]=i-mid;}while (hd<=tl&&que[hd]<i-f[i]) hd++;dp[i]=dp[i-1];if (hd<=tl) dp[i]=max(dp[i],dp[que[hd]]+i-que[hd]);}if (dp[n]+eps>=n*0.9) l=mid;else r=mid-1;}printf("%d\n",l);}
}