【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 }