广义后缀自动机+DP
对于作文库建出广义后缀自动机,广义自动机就是在每次添加一个字符串之前把(last=0),然后正常添加就好了
对于每个询问串,预处理出每个位置(i)能向前匹配的最长长度(pp[i])。
二分长度(L),对于位置(i),设往前匹配到(j),满足(i-pp[i]<=j<=i-L), 则中间新增的匹配长度为(i-j),前面的匹配长为(f[j]),则(f[i]=f[j]+i-j)。
因为(i)是每次(+1)的,(pp[i])每次有可能(+1),所以(i-pp[i])是单调不降的,可以用单调队列来维护(f[i]-i)的值
#include<map>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=2e6+100;
struct SAM{
int son[maxn][2],len[maxn],fa[maxn],pp[maxn];
int tot,last,m,st[maxn],l,r,f[maxn];
SAM(){tot=last=0,fa[0]=-1;}
void insert(int x){
int p=last,np=++tot;
len[np]=len[p]+1;
while(~p&&!son[p][x])
son[p][x]=np,p=fa[p];
if(p==-1)
fa[np]=0;
else{
int q=son[p][x];
if(len[q]==len[p]+1)
fa[np]=q;
else{
int nq=++tot;
memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq]=fa[q];
len[nq]=len[p]+1;
fa[q]=fa[np]=nq;
while(~p&&son[p][x]==q)
son[p][x]=nq,p=fa[p];
}
}
last=np;
}
void getlen(char *s,int n){
int x=0,cnt=0;
for(int i=1;i<=n;i++){
int v=s[i]-'0';
while(~x&&son[x][v]==0)
x=fa[x];
if(x==-1){
x=cnt=0;
pp[i]=0;
continue;
}
cnt=min(cnt,len[x])+1;
x=son[x][v];
pp[i]=cnt;
}
}
bool check(char *s,int n,int L){
st[0]=0,r=0,l=1,f[0]=0;
for(int i=1;i<=n;i++){
f[i]=f[i-1];
if(i-L<0) continue;
while(r>=l&&f[st[r]]-st[r]<=f[i-L]-i+L) r--;
st[++r]=i-L;
while(r>=l&&st[l]<i-pp[i]) l++;
if(r>=l)
f[i]=max(f[i],f[st[l]]+i-st[l]);
}
return 10*f[n]>=9*n;
}
}sam;
int n,m;
char s[maxn];
int main(){
// freopen("4022.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%s",s+1);
int len=strlen(s+1);
sam.m=max(sam.m,len);
sam.last=0;
for(int i=1;i<=len;i++) sam.insert(s[i]-'0');
}
while(n--){
scanf("%s",s+1);
int len=strlen(s+1);
sam.getlen(s,len);
int l=1,r=len+1,mid,ans;
while(l<r){
mid=l+r>>1;
if(sam.check(s,len,mid))
l=mid+1,ans=mid;
else
r=mid;
}
printf("%d
",ans);
}
return 0;
}