bzoj2806 [Ctsc2012]Cheat
Posted wfj_2048
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj2806 [Ctsc2012]Cheat相关的知识,希望对你有一定的参考价值。
Description
Input
第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库
的行数
接下来M行的01串,表示标准作文库
接下来N行的01串,表示N篇作文
Output
N行,每行一个整数,表示这篇作文的Lo 值。
Sample Input
10110
000001110
1011001100
Sample Output
HINT
输入文件不超过1100000字节
注意:题目有改动,可识别的长度不小于90%即可,而不是大于90%
正解:二分+后缀自动机+$dp$+单调队列。
这道题思路好妙啊,人蠢没办法。。
首先我们可以发现这道题满足可二分性,于是我们直接二分答案,判断合法性就行了。
二分出一个答案以后,我们可以知道,要满足条件,首先每组匹配的串长度必须$\geq mid$。
然后因为匹配的串的长度必须大于等于原串长的$90\%$,那么我们可以设一个$dp$状态。
$f[i]$表示前$i$个字符,最多能匹配的长度是多少,可以知道我们只要满足$f[n]\geq 0.9*n$就行了。
那么转移就比较好想了,$f[i]=f[j]+i-j$,当$i-len\leq j\leq i-mid$时成立。其中$len$为以当前字符为最后一个点的最大匹配长度。
匹配字符串还没讲。。我们直接对给出的$m$个串做一个广义后缀自动机,然后找最大匹配就在自动机上跳$parent$链就行了。
这个方程乍一看还是$O(n^{2})$的,但是我们可以发现转移方程只和$j$有关,那么我们可以用一个单调队列来优化转移。
然后这道题我们就做完了。。$bzoj$终于刷到$400$题了。。
1 #include <bits/stdc++.h> 2 #define il inline 3 #define RG register 4 #define ll long long 5 #define N (2200010) 6 7 using namespace std; 8 9 int ch[N][2],fa[N],l[N],f[N],st[N],sum[N],n,m,la,tot,len; 10 char s[N]; 11 12 il int gi(){ 13 RG int x=0,q=1; RG char ch=getchar(); 14 while ((ch<‘0‘ || ch>‘9‘) && ch!=‘-‘) ch=getchar(); 15 if (ch==‘-‘) q=-1,ch=getchar(); 16 while (ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-48,ch=getchar(); 17 return q*x; 18 } 19 20 il void add(RG int c){ 21 RG int p=la,np=ch[p][c]; la=np; 22 if (np && l[np]==l[p]+1) return; 23 np=++tot,l[np]=l[p]+1,la=np; 24 for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np; 25 if (!p){ fa[np]=1; return; } RG int q=ch[p][c]; 26 if (l[q]==l[p]+1) fa[np]=q; else{ 27 RG int nq=++tot; l[nq]=l[p]+1; 28 fa[nq]=fa[q],fa[q]=fa[np]=nq; 29 memcpy(ch[nq],ch[q],sizeof(ch[q])); 30 for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; 31 } 32 return; 33 } 34 35 il void getlen(){ 36 RG int p=1,L=0; 37 for (RG int i=1,c;i<=len;++i){ 38 c=s[i]-‘0‘; while (p && !ch[p][c]) p=fa[p]; 39 if (!p) p=1,L=0; else L=min(L,l[p])+1,p=ch[p][c]; sum[i]=L; 40 } 41 return; 42 } 43 44 il int check(RG int key){ 45 RG int h=1,t=0; 46 for (RG int i=1;i<=len;++i){ 47 f[i]=f[i-1]; if (i<key) continue; 48 while (h<=t && f[st[t]]-st[t]<=f[i-key]-i+key) --t; 49 st[++t]=i-key; while (h<=t && st[h]<i-sum[i]) ++h; 50 if (h<=t) f[i]=max(f[i],f[st[h]]-st[h]+i); 51 } 52 return f[len]*10>=len*9; 53 } 54 55 int main(){ 56 #ifndef ONLINE_JUDGE 57 freopen("cheat.in","r",stdin); 58 freopen("cheat.out","w",stdout); 59 #endif 60 n=gi(),m=gi(),tot=1; 61 for (RG int i=1;i<=m;++i){ 62 scanf("%s",s+1),len=strlen(s+1),la=1; 63 for (RG int j=1;j<=len;++j) add(s[j]-‘0‘); 64 } 65 while (n--){ 66 scanf("%s",s+1),len=strlen(s+1),getlen(); 67 RG int l=0,r=len,mid,ans=0; 68 while (l<=r) mid=(l+r)>>1,check(mid)?(ans=mid,l=mid+1):r=mid-1; 69 printf("%d\n",ans); 70 } 71 return 0; 72 }
以上是关于bzoj2806 [Ctsc2012]Cheat的主要内容,如果未能解决你的问题,请参考以下文章
[bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)
BZOJ2806[Ctsc2012]Cheat 广义后缀自动机+二分+单调队列优化DP