题目描述:就是给你多个01串当作字典然后给你另一个01串问你这个01串长度90%以上被匹配时的分段匹配的段的最大值
L0
(这个最大值是当前分段每一段的最小值,就是每一段长度都大于这个值,求这个值的最大值)
我看到这道题就知道要把所有的字典的串合成一个串来搞,原来弄过一个AC自动机的题目和这个描述很像,自然而然就这么弄了,建立SAM的时候每一个串之间弄个分隔符。原因不说了。现在我们只要先求出每一个位置向前能够匹配的最大值
f(i)
为什么只求最大值呢?因为每一个位置最大值能够向前匹配,那么小于最大值的串显然是最大值的后缀子串,显然仍然能够匹配,所以只需要求每个位置的能够匹配的最大值就可以了。然后怎么搞呢枚举每一段显然不可能,那这种情况下二分吧,二分当前的
L0
然后怎么检测当前的
L0
是否可用呢,另
dp(i)
为1-i位能够被
L0
匹配的最大数量
首先因为
dp(i)>=dp(i−1)
所以我们不用看
i−f(i)
之前的位置,因为
dp(i−f(i))>=dp(i−f(i)−k)
然后发现长度起码为
L0
所以我们只需要关注
[i−f(i),i−L0]
这个区间内的
dp
值就行了
所以让
dp(i)
最大那么
dp(i)=max(dp(j)+(i−j))i−f(i)−1<j<i−L0+1
那么这个算法的复杂度是
O(n2logn)
显然满足不了,那么进行优化
dp(i)=max(dp(j)−j)+i
那么
dp(i)−i=max(dp(j)−j)
那么我们另
g(i)=dp(i)−i
求每一个点之前的区间内的最大值,试试看单调队列优化,如果要用单调队列优化需要证明
i−f(i)
是单调递增的,需要证明
f(i)
是单调递减的那么我们发现如果有一个发现如果前一个串
a2a3...an
显然是
a2a3...anan+1
如果
a1a2a3...anan+1
可以被匹配那么显然
a1a2a3...an
也可以被匹配那么前一个就不是
a2a3...an
那么
f(i+1)<=f(i)+1
那么这个时候区间的左端点要么向右移动要么不动,然后现在用单调队列优化就行了,每次显然是多了一个
g(i−L0)
。。。wa了5个小时,发现是忘了清零。。。好了现在复杂度变成
O(nlogn)
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int MAXN = 2200010;
const int INF = 100000000;
struct node{
node *p[4], *pre;
int len;
}Edges[MAXN*2+10], *ecnt=Edges+1,*root=Edges,*last=Edges, *st[MAXN*2+10];
void Insert(int w){
node *np = ecnt++;
node *p = last;
np->len = p->len+1;
while(p&&!p->p[w])
p->p[w]=np, p=p->pre;
if(!p){
np->pre = root;
}else{
node *q = p->p[w];
if(p->len+1 == q->len){
np->pre = q;
}else{
node *nnd = ecnt++;
memcpy(nnd->p, q->p, sizeof (nnd->p));
nnd->len = p->len+1; nnd->pre = q->pre; q->pre = nnd; np->pre = nnd;
while(p&&p->p[w]==q)
p->p[w]=nnd, p=p->pre;
}
}
last = np;
}
const int MAXT = 2200010;
char s[MAXT+10];
int dp[MAXT+10], dp2[MAXT+10], que[MAXT];
bool check(int l){
//dp2[i] = max{dp2[j]+i-j} i-out dp2[i]-i = max{dp2[j]-j}//
int len = strlen(s);
int lk=0, rk=0, ins;
for(int i=0;i<l;i++)
dp2[i] = 0;
for(int i=l;i<=len;i++){
dp2[i] = dp2[i-1];
ins = i-l;
while(lk<rk&&dp2[ins]-ins>=dp2[que[rk-1]]-que[rk-1]) rk--;
que[rk++] = ins;
while(lk<rk&&que[lk]<i-dp[i]) lk++;
if(lk < rk)
dp2[i] = max(dp2[i], dp2[que[lk]] - que[lk] + i);
}
return dp2[len] * 10 >= len * 9;
}
int main(){
int n, m;
scanf("%d %d", &n, &m);
for(int i=0;i<m;i++){
scanf("%s", s);
int len = strlen(s);
for(int j=0;j<len;j++)
Insert(s[j]-'0');
Insert(2);
}
for(int i=0;i<n;i++){
node *now = root;
scanf("%s", s);
int L = 0, len = strlen(s);
for(int j=0;j<len;j++){
int idx = s[j]-'0';
if(now->p[idx]){
now = now->p[idx];
L++;
}else{
while(now && !now->p[idx])
now = now->pre;
if(!now){
now = root;
L = 0;
}else{
L = now->len + 1;
now = now->p[idx];
}
}
dp[j+1] = L;
}
int l=1, r=len, ans=0;
while(l <= r){
int mid = (l + r) >> 1;
if(check(mid)){ l = mid+1; ans = mid;
}else r = mid-1;
}
printf("%d\n", ans);
}
return 0;
}