[bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)
Posted rikurika
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)相关的知识,希望对你有一定的参考价值。
偷懒直接把bzoj的网页内容ctrlcv过来了
2806: [Ctsc2012]Cheat
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1943 Solved: 1004
[Submit][Status][Discuss]
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%
所谓少于$1.1*10^6$就是总输入字符数少于这个数...
果然CTSC出的题水平够高
专门爆踩SAM只学了板子不懂各个元素含义和应用的菜鸡(像我这样的就是了)
随便把模式串扔进广义SAM里
答案L0具有单调性
所以二分答案
接下来考虑$O(n)$求出最长匹配长度
$O(n)$的话...只能dp了啊
在SAM上跑字符串匹配求出每一位的最长向前匹配长度$f[i]$
用$dp[i]$表示到i位置匹配了多少字符
之后有dp方程
$dp[i]=max(dp[i-1],max(dp[(i-f[i]) to (i-L0)]))$
$O(n^2)$单调队列优化变成$O(n)$
本题完结
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 using std::queue; 5 const int N=1100011; 6 char sin[N]; 7 int nn,nm,n; 8 template<typename tp>tp max(tp a,tp b){return a>b?a:b;} 9 10 struct sam 11 { 12 int tranc[2],len,pre; 13 }s[N<<1]; 14 15 struct Sakuya_Brando 16 { 17 int size,fin; 18 Sakuya_Brando(){size=1;} 19 void insert(int ch) 20 { 21 int npx,npy,lpx,lpy; 22 if(s[fin].tranc[ch]) 23 { 24 lpx=fin,lpy=s[fin].tranc[ch]; 25 if(s[lpy].len==s[lpx].len+1) fin=lpy; 26 else 27 { 28 npy=++size; 29 s[npy]=s[lpy]; 30 s[npy].len=s[lpx].len+1; 31 s[lpy].pre=npy; 32 while(s[lpx].tranc[ch]==lpy) 33 { 34 s[lpx].tranc[ch]=npy; 35 lpx=s[lpx].pre; 36 } 37 fin=npy; 38 } 39 return; 40 } 41 npx=++size; 42 s[npx].len=s[fin].len+1; 43 for(lpx=fin;lpx&&!s[lpx].tranc[ch];lpx=s[lpx].pre) s[lpx].tranc[ch]=npx; 44 if(!lpx) s[npx].pre=1; 45 else 46 { 47 lpy=s[lpx].tranc[ch]; 48 if(s[lpy].len==s[lpx].len+1) s[npx].pre=lpy; 49 else 50 { 51 npy=++size; 52 s[npy]=s[lpy]; 53 s[npy].len=s[lpx].len+1; 54 s[lpy].pre=s[npx].pre=npy; 55 while(s[lpx].tranc[ch]==lpy) 56 { 57 s[lpx].tranc[ch]=npy; 58 lpx=s[lpx].pre; 59 } 60 } 61 } 62 fin=npx; 63 } 64 void ins(char *str,int len) 65 { 66 fin=1; 67 len=strlen(str); 68 for(int i=0;i<len;i++) insert(str[i]-‘0‘); 69 } 70 }sakuya; 71 72 int f[N]; 73 int dp[N]; 74 struct stack 75 { 76 int v,id; 77 stack(){} 78 stack(int a,int b){v=a,id=b;} 79 }sta[N]; 80 int he,ta; 81 82 void bao0(char *str,int len) 83 { 84 int px=1,tmp=0; 85 for(int i=0;i<len;i++) 86 { 87 int ch=str[i]-‘0‘; 88 if(s[px].tranc[ch]) tmp++,px=s[px].tranc[ch]; 89 else 90 { 91 while(px&&!s[px].tranc[ch]) px=s[px].pre; 92 if(!px) tmp=0,px=1; 93 else tmp=s[px].len+1,px=s[px].tranc[ch]; 94 } 95 f[i]=tmp; 96 } 97 } 98 void noi(char *str,int len) 99 { 100 for(int i=0;i<len;i++) f[i]=0; 101 } 102 bool check(char *str,int ll,int len) 103 { 104 he=1,ta=0; 105 int mxx=0; 106 for(int i=ll,j=ll-1;j<len;i++,j++) 107 { 108 dp[i]=dp[i-1]; 109 while(he<=ta&&sta[he].id<i-f[j]) he++; 110 while(he<=ta&&sta[ta].v<=dp[i-ll]-(i-ll)) ta--; 111 sta[++ta]=stack(dp[i-ll]-(i-ll),i-ll); 112 while(he<=ta&&sta[he].id<i-f[j]) he++; 113 if(he<=ta) dp[i]=max(dp[i],i+sta[he].v); 114 } 115 mxx=dp[len]; 116 for(int i=0;i<=len;i++) dp[i]=0; 117 if(mxx*10>=len*9) return 1; 118 else return 0; 119 } 120 121 int main() 122 { 123 scanf("%d%d",&nn,&nm); 124 for(int i=1;i<=nm;i++) 125 { 126 scanf("%s",sin); 127 n=strlen(sin); 128 sakuya.ins(sin,n); 129 } 130 for(int i=1;i<=nn;i++) 131 { 132 scanf("%s",sin); 133 n=strlen(sin); 134 bao0(sin,n); 135 int l0=0,r0=n; 136 while(l0<r0) 137 { 138 int mid=(l0+r0+1)>>1; 139 if(check(sin,mid,n)) l0=mid; 140 else r0=mid-1; 141 } 142 noi(sin,n); 143 printf("%d\n",l0); 144 } 145 return 0; 146 }
以上是关于[bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)的主要内容,如果未能解决你的问题,请参考以下文章
[bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)
BZOJ2806[Ctsc2012]Cheat 广义后缀自动机+二分+单调队列优化DP