【BZOJ 2806】 2806: [Ctsc2012]Cheat (SAM+二分+DP+单调队列)

2806: [Ctsc2012]Cheat

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 1262  Solved: 643

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%

Source

 

 

【分析】

  显然可以二分。

  然后就是判断。

  先用SAM找到i最长向左可以匹配的位置l,决策区间就是[i-l,i-lim],思考一下发现这个决策区间是不断向右移动的,就是类似滑动窗口?这个就可以用单调队列了。

  即f[i]=max{f[i-1],f[j]+j-i|i-l<=j<=i-lim}

 

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 #define Maxn 2500010
  8 
  9 int mymax(int x,int y) {return x>y?x:y;}
 10 
 11 struct node
 12 {
 13     int son[4],pre,step;
 14 }t[Maxn*2];
 15 
 16 struct sam
 17 {
 18     int tot,last;
 19     void extend(int k)
 20     {
 21         int np=++tot,p=last;
 22         t[np].step=t[p].step+1;
 23         while(p&&!t[p].son[k])
 24         {
 25             t[p].son[k]=np;
 26             p=t[p].pre;
 27         }
 28         if(!p) t[np].pre=1;
 29         else
 30         {
 31             int q=t[p].son[k];
 32             if(t[q].step==t[p].step+1) t[np].pre=q;
 33             else
 34             {
 35                 int nq=++tot;
 36                 memcpy(t[nq].son,t[q].son,sizeof(t[nq].son));
 37                 t[nq].step=t[p].step+1;
 38                 t[nq].pre=t[q].pre;
 39                 t[q].pre=t[np].pre=nq;
 40                 while(p&&t[p].son[k]==q)
 41                 {
 42                     t[p].son[k]=nq;
 43                     p=t[p].pre;
 44                 }
 45             }
 46         }
 47         last=np;
 48     }
 49 }sam;
 50 
 51 char s[Maxn];
 52 
 53 int q[Maxn*2];
 54 int f[Maxn];
 55 bool check(int x,int ll)
 56 {
 57     int l=1,r=1;
 58     int nw=1,sm=0;
 59     for(int i=1;i<=ll;i++) f[i]=0;
 60     q[1]=0;
 61     for(int i=1;i<=ll;i++)
 62     {
 63         f[i]=f[i-1];
 64         int ind=s[i]-'0';
 65         while(nw&&!t[nw].son[ind]) nw=t[nw].pre,sm=t[nw].step;
 66         if(t[nw].son[ind]) nw=t[nw].son[ind],sm++;
 67         else {nw=1;sm=0;continue;}
 68         while(i-x>=0&&l<=r&&f[i-x]-(i-x)>f[q[r]]-(q[r])) r--;
 69         if(i-x>=0) q[++r]=i-x;
 70         while(l<=r&&(q[l]<i-sm||q[l]>i-x)) l++;
 71         if(l<=r) f[i]=mymax(f[i],f[q[l]]+i-q[l]);
 72     }
 73     return f[ll]*10>=ll*9;
 74 }
 75 
 76 int main()
 77 {
 78     int n,m;
 79     scanf("%d%d",&n,&m);
 80     sam.tot=sam.last=1;
 81     for(int i=1;i<=m;i++)
 82     {
 83         scanf("%s",s);
 84         int ll=strlen(s);
 85         for(int j=0;j<ll;j++) sam.extend(s[j]-'0');
 86         // sam.extend(2);
 87         sam.last=1;
 88     }
 89     for(int i=1;i<=n;i++)
 90     {
 91         scanf("%s",s+1);
 92         int ll=strlen(s+1);
 93         int l=0,r=ll;
 94         while(l<r)
 95         {
 96             int mid=(l+r+1)>>1;
 97             if(check(mid,ll)) l=mid;
 98             else r=mid-1;
 99         }
100         printf("%d\n",l);
101     }
102     return 0;
103 }
View Code

【打的是广义SAM?

 

2017-04-18 12:55:48

posted @ 2017-04-18 12:55  konjak魔芋  阅读(165)  评论(0编辑  收藏  举报