【BZOJ 2806】 2806: [Ctsc2012]Cheat (SAM+二分+DP+单调队列)
2806: [Ctsc2012]Cheat
Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 1262 Solved: 643Description
Input
第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库
的行数
接下来M行的01串,表示标准作文库
接下来N行的01串,表示N篇作文Output
N行,每行一个整数,表示这篇作文的Lo 值。
Sample Input
1 2
10110
000001110
1011001100Sample Output
4HINT
输入文件不超过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 }
【打的是广义SAM?
2017-04-18 12:55:48