bzoj2806: [Ctsc2012]Cheat
地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2806
题目:
2806: [Ctsc2012]Cheat
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1457 Solved: 738
Description
Input
第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库
的行数
接下来M行的01串,表示标准作文库
接下来N行的01串,表示N篇作文
Output
N行,每行一个整数,表示这篇作文的Lo 值。
Sample Input
1 2
10110
000001110
1011001100
10110
000001110
1011001100
Sample Output
4
HINT
输入文件不超过1100000字节
注意:题目有改动,可识别的长度不小于90%即可,而不是大于90%
思路:
对标准串建立广义后缀自动机,然后让作文串在上面跑,记录长度每个位置所能匹配的最长长度,记为mx[i].
求L0的过程显然可以二分,然后用dp判断是否可行。
dp转移很容易可以得到:dp[i]=max(dp[i-1],max(dp[j]+i-j)) i-mx[i]<=j<=i-L
如果直接dp复杂度极限是O(n^2),显然TLE。
观察dp方程可以发现 dp[j] - j 部分和i无关,且 i - mx[i] 和 i - L是不递减的。
所以可以通过维护个值递增的单调队列优化。
广义后缀自动机就是插入一个串后把last标回root,然后插入下一个串。
1 /************************************************************** 2 Problem: 2806 3 User: weeping 4 Language: C++ 5 Result: Accepted 6 Time:976 ms 7 Memory:68676 kb 8 ****************************************************************/ 9 10 #include <bits/stdc++.h> 11 using namespace std; 12 struct SAM 13 { 14 static const int MAXN = 1000001<<1;//大小为字符串长度两倍 15 static const int LetterSize = 2; 16 17 int tot, last, ch[MAXN][LetterSize], fa[MAXN], len[MAXN]; 18 int sum[MAXN], tp[MAXN], cnt[MAXN]; //sum,tp用于拓扑排序,tp为排序后的数组 19 20 void init( void) 21 { 22 last = tot = 1; 23 len[1] = 0; 24 memset( ch[1], 0, sizeof ch[1]); 25 } 26 27 void add( int x) 28 { 29 int p = last, np = last = ++tot; 30 len[np] = len[p] + 1, cnt[np] = 1; 31 memset( ch[np], 0, sizeof ch[np]); 32 while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; 33 if( p == 0) 34 fa[np] = 1; 35 else 36 { 37 int q = ch[p][x]; 38 if( len[q] == len[p] + 1) 39 fa[np] = q; 40 else 41 { 42 int nq = ++tot; 43 memcpy( ch[nq], ch[q], sizeof ch[q]); 44 len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; 45 while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; 46 } 47 } 48 } 49 50 void toposort( void) 51 { 52 for(int i = 1; i <= len[last]; i++) sum[i] = 0; 53 for(int i = 1; i <= tot; i++) sum[len[i]]++; 54 for(int i = 1; i <= len[last]; i++) sum[i] += sum[i-1]; 55 for(int i = 1; i <= tot; i++) tp[sum[len[i]]--] = i; 56 for(int i = tot; i; i--) cnt[fa[tp[i]]] += cnt[tp[i]]; 57 } 58 59 int mx[1000002],q[1000001],dp[1000001]; 60 bool check(int x,int slen) 61 { 62 if(!x) return 1; 63 int st=0,se=0; 64 for(int i=0;i<x;i++) dp[i]=0; 65 for(int i=1;i<=slen;i++) 66 { 67 while(st>se&&i-x>0&&dp[q[st]]-q[st]<=dp[i-x]-(i-x)) st--; 68 if(i-x<0) continue; 69 q[++st]=i-x; 70 while(st>se&&q[se+1]<i-mx[i]) se++; 71 dp[i]=dp[i-1]; 72 if(st>se) 73 dp[i]=max(dp[i],dp[q[se+1]]-q[se+1]+i); 74 } 75 if(dp[slen]*10>=9*slen) return 1; 76 return 0; 77 } 78 void go(char *ss) 79 { 80 int slen=strlen(ss+1); 81 for(int i=1,p=1,num=0;i<=slen;i++) 82 { 83 int c=ss[i]-'0'; 84 if(ch[p][c]) p=ch[p][c],num++; 85 else 86 { 87 while(p&&!ch[p][c]) p=fa[p]; 88 if(!p) p=1,num=0; 89 else num=len[p]+1,p=ch[p][c]; 90 } 91 mx[i]=num; 92 } 93 int l=0,r=slen,ans; 94 while(l<=r) 95 { 96 int mid=l+r>>1; 97 if(check(mid,slen)) ans=mid,l=mid+1; 98 else r=mid-1; 99 } 100 printf("%d\n",ans); 101 } 102 } sam; 103 char ss[1000001]; 104 int main(void) 105 { 106 //freopen("in.acm","r",stdin); 107 int n,m; 108 cin>>m>>n; 109 sam.init(); 110 for(int i=1;i<=n;i++,sam.last=1) 111 { 112 scanf("%s",ss); 113 for(int j=0,len=strlen(ss);j<len;j++) sam.add(ss[j]-'0'); 114 } 115 for(int i=1;i<=m;i++) 116 scanf("%s",ss+1),sam.go(ss); 117 return 0; 118 }
作者:weeping
出处:www.cnblogs.com/weeping/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。