BZOJ2806:[CTSC2012]Cheat(广义SAM,二分,DP)
Description
Input
第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库的行数
接下来M行的01串,表示标准作文库
接下来N行的01串,表示N篇作文
Output
N行,每行一个整数,表示这篇作文的L0值。
Sample Input
1 2
10110
000001110
1011001100
10110
000001110
1011001100
Sample Output
4
HINT
输入文件不超过1100000字节
注意:题目有改动,可识别的长度不小于90%即可,而不是大于90%
Solution
首先把广义$SAM$建出来,然后考虑对于一个询问,
我们可以把这个作文放到$SAM$上跑,就可以求得这个字符串的每个位置向前延伸仍然可以匹配的最大长度,记为$Len[i]$。
因为直接求答案不好求,所以我们二分一个答案$lim$
剩下的就是$DP$了。$f[i]$表示到第$i$个位置最多能匹配多少,转移显然。
$f[i]=max(f[i-1]),\sum_{j=lim}^{len} f[i-j]+j$。 单调队列优化一下即可。
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define N (2200009) 5 using namespace std; 6 7 int n,m,len,Len[N],f[N],q[N]; 8 char s[N]; 9 10 struct SAM 11 { 12 int son[N][2],fa[N],step[N]; 13 int p,q,np,nq,last,cnt; 14 SAM(){last=cnt=1;} 15 16 void Insert(int x) 17 { 18 p=last; np=last=++cnt; step[np]=step[p]+1; 19 while (p && !son[p][x]) son[p][x]=np,p=fa[p]; 20 if (!p) fa[np]=1; 21 else 22 { 23 q=son[p][x]; 24 if (step[q]==step[p]+1) fa[np]=q; 25 else 26 { 27 nq=++cnt; step[nq]=step[p]+1; 28 memcpy(son[nq],son[q],sizeof(son[q])); 29 fa[nq]=fa[q]; fa[q]=fa[np]=nq; 30 while (son[p][x]==q) son[p][x]=nq,p=fa[p]; 31 } 32 } 33 } 34 void Find(char s[]) 35 { 36 int now=1,len=0; 37 for (int i=0,l=strlen(s); i<l; ++i) 38 { 39 int c=s[i]-'0'; 40 if (son[now][c]) ++len,now=son[now][c]; 41 else 42 { 43 while (now && !son[now][c]) now=fa[now]; 44 if (!now) len=0,now=1; 45 else len=step[now]+1,now=son[now][c]; 46 } 47 Len[i+1]=len; 48 } 49 } 50 }SAM; 51 52 bool check(int lim) 53 { 54 int head=1,tail=0,len=strlen(s); 55 for (int i=1; i<=len; ++i) 56 { 57 f[i]=f[i-1]; 58 if (i-lim<0) continue; 59 while (head<=tail && f[q[tail]]-q[tail]<=f[i-lim]-i+lim) --tail; 60 q[++tail]=i-lim; 61 while (head<=tail && q[head]<i-Len[i]) ++head; 62 if (head<=tail) f[i]=max(f[i],f[q[head]]+i-q[head]); 63 } 64 return f[len]*10>=len*9; 65 } 66 67 int main() 68 { 69 scanf("%d%d",&n,&m); 70 for (int i=1; i<=m; ++i,SAM.last=1) 71 { 72 scanf("%s",s); 73 for (int j=0,l=strlen(s); j<l; ++j) 74 SAM.Insert(s[j]-'0'); 75 } 76 for (int i=1; i<=n; ++i) 77 { 78 scanf("%s",s); 79 SAM.Find(s); 80 int l=1,r=strlen(s),ans=0; 81 while (l<=r) 82 { 83 int mid=(l+r)>>1; 84 if (check(mid)) ans=mid,l=mid+1; 85 else r=mid-1; 86 } 87 printf("%d\n",ans); 88 } 89 }