bzoj2806: [Ctsc2012]Cheat

地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2806

题目:

2806: [Ctsc2012]Cheat

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 1457  Solved: 738

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%

 

思路:

  对标准串建立广义后缀自动机,然后让作文串在上面跑,记录长度每个位置所能匹配的最长长度,记为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 }

 

posted @ 2017-09-18 23:55  weeping  阅读(158)  评论(0编辑  收藏  举报