【BZOJ2806】【CTSC2012】Cheat - 广义后缀自动机+单调队列优化DP

题意:

Description

Input

第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库的行数
接下来M行的01串,表示标准作文库
接下来N行的01串,表示N篇作文

Output

N行,每行一个整数,表示这篇作文的Lo 值。

HINT

输入文件不超过1100000字节

注意:题目有改动,可识别的长度不小于90%即可,而不是大于90%

题解:

好题!

先对标准作文库建出广义SAM,把每个作文串在SAM上匹配,求出每一位之前最长出现过子串长度$s[i]$;

显然没有什么好办法直接求L,考虑二分答案判断是否可行;

设当前二分值为$mid$,$f[i]$表示前$i$位最长的熟悉长度,则易得DP方程:$f[i]=max\{f[j]+i-j\}$,决策区间就是$[i-s[i],i-mid]$;

显然$i-s[i]$单调不降,所以可以用单调队列优化到$O(n)$

最后判一下比例是否大于$90\%$即可。

代码:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 using namespace std;
10 typedef long long ll;
11 typedef double db;
12 int n,m,l,r,ans,len,tot=1,last,rt=1,son[2200001][2],fa[2200001],mx[2200001],s[1100001],q[1100001],f[1100001];
13 char st[1100001];
14 void extend(int ch){
15     int p=last,np=++tot;
16     mx[np]=mx[p]+1;
17     for(;p&&!son[p][ch];p=fa[p])son[p][ch]=np;
18     if(!p)fa[np]=rt;
19     else{
20         int q=son[p][ch];
21         if(mx[q]==mx[p]+1)fa[np]=q;
22         else{
23             int nq=++tot;
24             mx[nq]=mx[p]+1;
25             memcpy(son[nq],son[q],sizeof(son[q]));
26             fa[nq]=fa[q];
27             fa[q]=fa[np]=nq;
28             for(;p&&son[p][ch]==q;p=fa[p])son[p][ch]=nq;
29         }
30     }
31     last=np;
32 }
33 void pre(char *st,int len){
34     int nw=rt,tmp=0;
35     for(int i=1;i<=len;i++){
36         if(son[nw][st[i]-'0']){
37             nw=son[nw][st[i]-'0'];
38             tmp++;
39         }else{
40             for(;nw&&!son[nw][st[i]-'0'];nw=fa[nw]);
41             if(!nw){
42                 nw=rt;
43                 tmp=0;
44             }else{
45                 tmp=mx[nw]+1;
46                 nw=son[nw][st[i]-'0'];
47             }
48         }
49         s[i]=tmp;
50     }
51 }
52 bool check(int k){
53     int L=1,R=0;
54     for(int i=1;i<=len;i++){
55         f[i]=f[i-1];
56         if(i<k)continue;
57         for(;L<=R&&f[q[R]]-q[R]<f[i-k]-i+k;R--);
58         q[++R]=i-k;
59         for(;L<=R&&q[L]<i-s[i];L++);
60         if(L<=R)f[i]=max(f[i],f[q[L]]-q[L]+i);
61     }
62     return f[len]*10>=len*9;
63 }
64 int main(){
65     scanf("%d%d",&n,&m);
66     for(int i=1;i<=m;i++){
67         scanf("%s",st+1);
68         len=strlen(st+1);
69         last=rt;
70         for(int j=1;j<=len;j++){
71             extend(st[j]-'0');
72         }
73     }
74     for(int i=1;i<=n;i++){
75         scanf("%s",st+1);
76         len=strlen(st+1);
77         pre(st,len);
78         l=ans=0,r=len;
79         while(l<=r){
80             int mid=(l+r)/2;
81             if(check(mid))ans=mid,l=mid+1;
82             else r=mid-1;
83         }
84         printf("%d\n",ans);
85     }
86     return 0;
87 }
posted @ 2018-12-22 11:00 DCDCBigBig 阅读(...) 评论(...) 编辑 收藏