1. 程式人生 > >BZOJ2806: [Ctsc2012]Cheat(廣義字尾自動機,單調佇列優化Dp)

BZOJ2806: [Ctsc2012]Cheat(廣義字尾自動機,單調佇列優化Dp)

Description

Input

第一行兩個整數N,M表示待檢查的作文數量,和小強的標準作文庫
的行數
接下來M行的01串,表示標準作文庫
接下來N行的01串,表示N篇作文

Output

N行,每行一個整數,表示這篇作文的Lo 值。

Sample Input

1 2
10110
000001110
1011001100

Sample Output

4

解題思路:

L0值具有單調性。

L0值為0時,一定有匹配,為1時只需要考慮字符集,為2時要考慮前後順序,所以具有單調性,L0越小匹配長度越大,那麼可以二分。

這道題要求不能覆蓋,所以不能使用簡單的Dp來解決,但也很明顯,得知一個字串某一位為結尾時最長匹配長度是很有用的QAQ

所以設f[i]為以文字串i結尾,最長可識別子串的長度,那麼startpos就是i-f[i],設Dp[i]表示匹配到i最長(可以不連續,但不小於L0)的最大匹配長度。

為了實現可不連續,Dp[i]初值為Dp[i-1],所以Dp[i]的轉移方程就是Dp[i]=max(Dp[i-1],max({Dp[j]+i-j|i-j>=L0}))

轉移是O(n2)的過不了,可以將Dp[j]-j與i分離將Dp[j]-j用單調佇列維護,就是O(n)的了^_^

程式碼:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4
struct sant{ 5 int tranc[26]; 6 int len; 7 int pre; 8 }s[2100000]; 9 int siz; 10 int fin; 11 int n,m; 12 char tmp[2100000]; 13 int maxl[2100000]; 14 int dp[3000000]; 15 int x[2000000]; 16 void Insert(int c) 17 { 18 int nwp,nwq,lsp,lsq; 19 nwp=++siz; 20 s[nwp].len=s[fin].len+1
; 21 for(lsp=fin;lsp&&!s[lsp].tranc[c];lsp=s[lsp].pre) 22 s[lsp].tranc[c]=nwp; 23 if(!lsp) 24 s[nwp].pre=1; 25 else{ 26 lsq=s[lsp].tranc[c]; 27 if(s[lsq].len==s[lsp].len+1) 28 s[nwp].pre=lsq; 29 else{ 30 nwq=++siz; 31 s[nwq]=s[lsq]; 32 s[nwq].len=s[lsp].len+1; 33 s[nwp].pre=s[lsq].pre=nwq; 34 while(s[lsp].tranc[c]==lsq) 35 { 36 s[lsp].tranc[c]=nwq; 37 lsp=s[lsp].pre; 38 } 39 } 40 } 41 fin=nwp; 42 return ; 43 } 44 bool can(int L0,int len) 45 { 46 dp[0]=0; 47 int t=0,h=1; 48 for(int i=1;i<=len;i++) 49 { 50 dp[i]=dp[i-1]; 51 if(i<L0) 52 continue; 53 while(t>=h&&dp[x[t]]-x[t]<=dp[i-L0]-i+L0)t--; 54 x[++t]=i-L0; 55 while(t>=h&&x[h]<i-maxl[i])h++; 56 if(t>=h) 57 dp[i]=std::max(dp[x[h]]+i-x[h],dp[i]); 58 } 59 return 10*dp[len]>=9*len; 60 } 61 int main() 62 { 63 fin=++siz; 64 scanf("%d%d",&n,&m); 65 for(int i=1;i<=m;i++) 66 { 67 scanf("%s",tmp+1); 68 int len=strlen(tmp+1); 69 fin=1; 70 for(int j=1;j<=len;j++) 71 Insert(tmp[j]-'0'); 72 } 73 while(n--) 74 { 75 scanf("%s",tmp+1); 76 int len=strlen(tmp+1); 77 int root=1; 78 int mxl=0; 79 for(int i=1;i<=len;i++) 80 { 81 int c=tmp[i]-'0'; 82 if(s[root].tranc[c]) 83 { 84 root=s[root].tranc[c]; 85 mxl++; 86 }else{ 87 while(!s[root].tranc[c]) 88 root=s[root].pre; 89 if(!root) 90 { 91 root=1; 92 mxl=0; 93 }else{ 94 mxl=s[root].len+1; 95 root=s[root].tranc[c]; 96 } 97 } 98 maxl[i]=mxl; 99 } 100 int ans=0; 101 int l=0,r=len; 102 while(l<=r) 103 { 104 int mid=(l+r)>>1; 105 if(can(mid,len)) 106 { 107 ans=mid; 108 l=mid+1; 109 }else 110 r=mid-1; 111 } 112 printf("%d\n",ans); 113 } 114 return 0; 115 }