您的位置:首页 > 其它

[BZOJ2806][Ctsc2012]Cheat(后缀自动机+单调队列优化dp)

2018-02-27 16:15 549 查看

题目:

我是超链接

题解:

我们先把标准串建出一个广义后缀自动机

二分一个L,用dp判断可行性,dp?!

首先我们要用后缀自动机预处理出l[i],表示第i位一定选,可以匹配上的最长长度,即向前延伸最远可以和标准串匹配的长度

怎么用dp判可行啊?f[i]表示前i位能称为【熟悉】的最大长度,那么最后用f
和len比一比看看到不到90%就好,f[i]怎么求呢?

不难写出转移方程f[i]=max{f[i-1],f[j]+i-j} j∈[i−l[i],i−L]j∈[i−l[i],i−L]

这个范围是需要我们注意的,i-l[i]是因为过了这个界就不能匹配了,也就是不能用i-j来表示长度了;i-L是被称为【熟悉】的限制

O(n2)O(n2)?不行啊我们还是优化下看看能不能快一点

这个f[i]=f[i-1]可以赋为初值,当然如果i < L的话就不可能被称为【熟悉】了直接过

l[i]+1>=l[i+1]l[i]+1>=l[i+1]

i+1−l[i]−1<=i+1−l[i+1]i+1−l[i]−1<=i+1−l[i+1]

i−l[i]<=i+1−l[i+1]i−l[i]<=i+1−l[i+1]

所以i-l[i]是单调不降的,我们可以用单调队列维护区间f[i]-i的最值。

我们之前已经说过了,i结点的转移区间只有 [i−l[i],i−L]

每当i在向后移动的时候,唯一可能产生的新的转移点就是(i−L)

那我们就在队尾插入这个转移点:i−L

记住要先加入一个新点然后再处理head,why?因为i-l[i]不一定比i-L小,把head放在后面又处理掉一批不合法的情况。

信心满满交了上去,华丽丽的M了。。。。看到只有0/1之后眼泪掉下来

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=2200005;
int np,p,nq,q,last,cnt,ch
[2],fa
,step
,l
,len,f
,que
;
char st
;
void insert(int c)
{
p=last; np=last=++cnt;
step[np]=step[p]+1;
while (p && !ch[p][c]) ch[p][c]=np,p=fa[p];
if (!p) {fa[np]=1;return;}
q=ch[p][c];
if (step[q]==step[p]+1){fa[np]=q;return;}
nq=++cnt; step[nq]=step[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q]; fa[q]=fa[np]=nq;
while (ch[p][c]==q) ch[p][c]=nq,p=fa[p];
}
void init()
{
p=1;int tmp=0;
for (int i=1;i<=len;i++)
{
int c=st[i]-'0';
if (ch[p][c]) p=ch[p][c],tmp++;
else
{
while (p && !ch[p][c]) p=fa[p];
if (!p) p=1,tmp=0;
else tmp=step[p]+1,p=ch[p][c];
}
l[i]=tmp;
}
}
bool check(int L)
{
if (L==0) return 1;
f[0]=0;
int head=1,tail=0;
for (int i=1;i<=len;i++)
{
f[i]=f[i-1];
if (i<L) continue;
while (head<=tail && f[que[tail]]-que[tail]-L<=f[i-L]-i) tail--;
que[++tail]=i-L;
while (head<=tail && que[head]<i-l[i]) head++;
if (head<=tail) f[i]=max(f[i],f[que[head]]+i-que[head]);
}
return f[len]*10>=len*9;
}
void solve()
{
init();
int l=0,r=len,ans;
while (l<=r)
{
int mid=(l+r)>>1;
if (check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
cnt=1;
for (int i=1;i<=m;i++)
{
scanf("%s",st+1);
int l=strlen(st+1);last=1;
for (int j=1;j<=l;j++) insert(st[j]-'0');
}
for (int i=1;i<=n;i++)
{
scanf("%s",st+1);
len=strlen(st+1);
solve();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: