bzoj2806 [Ctsc2012]Cheat

Description

Input

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

Output

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

Sample Input

1 2
10110
000001110
1011001100

Sample Output

4

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 }

 

posted @ 2017-10-06 20:22  wfj_2048  阅读(159)  评论(0编辑  收藏  举报