题目链接
题解:
首先用作文库建出广义
S
A
M
SAM
SAM,然后对于每次询问我们可以二分最长长度
L
L
L,预处理出一个
m
a
x
l
e
n
maxlen
maxlen数组表示当前在字符串S的第
i
i
i个字符在广义
S
A
M
SAM
SAM上能匹配的最长长度,考虑怎么
c
h
e
c
k
check
check,设
d
p
[
i
]
dp[i]
dp[i] 表示当前处理到第
i
i
i个字符,前i个字符的最大匹配长度,容易发现,对i有贡献的区间位于
(
i
−
m
a
x
l
e
n
[
i
]
≤
j
≤
i
−
L
)
(i-maxlen[i] ≤j≤i-L)
(i−maxlen[i]≤j≤i−L)
则有
d
p
[
i
]
=
m
a
x
(
d
p
[
i
]
,
d
p
[
j
]
+
i
−
j
)
dp[i] = max(dp[i],dp[j]+i-j)
dp[i]=max(dp[i],dp[j]+i−j),
(
i
−
m
a
x
l
e
n
[
i
]
≤
j
≤
i
−
L
)
(i-maxlen[i]≤j≤i-L)
(i−maxlen[i]≤j≤i−L)
考虑转移
d
p
[
i
]
=
i
+
max
(
d
p
[
j
]
−
j
)
dp[i] = i+\max (dp[j]-j)
dp[i]=i+max(dp[j]−j),
(
i
−
m
a
x
l
e
n
[
i
]
≤
j
≤
i
−
L
)
(i-maxlen[i]≤j≤i-L)
(i−maxlen[i]≤j≤i−L)
所以单调队列维护
d
p
[
j
[
−
j
dp[j[-j
dp[j[−j最大值
每次将不可能造成贡献的点直接
p
o
p
pop
pop就好了,然后用队列中的点更新就好了。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2200050;
int nxt[MAXN][26],fa[MAXN],len[MAXN];
int max_len[MAXN],dp[MAXN];
int last=1,tot=1;
char s[MAXN];
inline void Insert(int x){
int p=last,np=++tot;
last=np,len[np]=len[p]+1;
for(;p&&!nxt[p][x];p=fa[p]) nxt[p][x]=np;
if(!p) fa[np]=1;
else{
int q = nxt[p][x];
if(len[p]+1==len[q]) fa[np]=q;
else{
int nq = ++tot;
len[nq]=len[p]+1;
memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for(;nxt[p][x]==q;p=fa[p]) nxt[p][x]=nq;
}
}
}
inline bool check(int lim,int len){
dp[0]=0;
queue<int> que;
for(int i=1;i<=len;i++){
dp[i]=dp[i-1];
if(i<lim) continue;
int p = i-lim;
while(!que.empty() && dp[que.front()]-que.front() < dp[p]-p) que.pop();
que.push(p);
while(!que.empty() && i-max_len[i]>que.front()) que.pop();
if(!que.empty()) dp[i] = max(dp[i],dp[que.front()]+i-que.front());
}
return 10*dp[len] >= len*9;
}
int main(){
int n,m; scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%s",s+1);
last=1; int l = strlen(s+1);
for(int j=1;j<=l;j++) Insert(s[j]-'0');
}
for(int i=1;i<=n;i++){
scanf("%s",s+1);
int sz = 0;
int l = strlen(s+1),p = 1;
for(int j=1;j<=l;j++){
if(nxt[p][s[j]-'0']) sz++,p = nxt[p][s[j]-'0'];
else{
while(p && !nxt[p][s[j]-'0']) p = fa[p];
if(!p) sz=0,p = 1;
else sz = len[p]+1,p = nxt[p][s[j]-'0'];
}
max_len[j] = sz;
}
int ll = 1,rr = l,res = 0;
while(ll<=rr){
int mid = (ll+rr)>>1;
if(check(mid,l)) ll=mid+1,res=mid;
else rr=mid-1;
}
printf("%d\n",res);
}
return 0;
}