被(SAM)和(dp)爆切就很难受
首先我们发现这个(L)显然有单调性,考虑二分
设 (dp_i) 表示匹配到第 (i) 位的最大匹配长度,然后处理 (len_i) 表示第 (i) 位能匹配到的最长子串
然后可以设计状态转移方程
首先很显然的 (dp_i=dp_{i-1})
然后考虑其他转移 (dp_i=minlimits_{i-len_i le jle i-mid}dp_j+i-j)
这个 (dp) 是 (O(n^2)) 的,考虑优化
发现可选区间右端点单调,且每个 (dp) 之间的优劣不会改变,可以用单调队列优化
复杂度(O(sum|T|log|T|))
AC代码
#include <bits/stdc++.h>
#define ll long long
#define cri const register int
#define re register
#define len(tt) t[tt].len
#define c(xx,yy) t[xx].ch[yy]
#define f(xx) t[xx].f
using namespace std;
struct node{
int ch[3],f,len;
}t[2200010];
int tot=1,rt,las,L[1200010],dp[1200010];
inline void add(cri x){
int p=las,np=las=++tot;
len(np)=len(p)+1;
for(;p&&!c(p,x);p=f(p)) c(p,x)=np;
if(!p) f(np)=rt;
else{
int q=c(p,x);
if(len(q)==len(p)+1) f(np)=q;
else {
int nq=++tot;t[nq]=t[q];
len(nq)=len(p)+1;
f(np)=f(q)=nq;
for(;p&&c(p,x)==q;p=f(p)) c(p,x)=nq;
}
}
}
int LEN,que[1100010],n;
char s[1100010];
inline void match(){
int len=0,now=rt;
for(int i=1;i<=LEN;i++){
int in=s[i]-'0';
if(c(now,in)) now=c(now,in),len++;
else{
for(;now&&!c(now,in);now=f(now));
if(!now) now=rt,len=0;
else len=len(now)+1,now=c(now,in);
}
L[i]=len;
}
}
inline bool check(cri mid){
int now=rt,l=1,r=0;
for(int i=1;i=0.9L*LEN;
}
int main(){
int N,M;
scanf("%d%d",&N,&M);
for(int i=1;i<=M;i++){
rt=las=1;
scanf("%s",s+1);LEN=strlen(s+1);
for(int j=1;j<=LEN;j++) add(s[j]-'0');
add(2);
}
for(int i=1;i<=N;i++){
scanf("%s",s+1);LEN=strlen(s+1);
match();
int l=0,r=LEN;
while(l>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d
",l);
}
}