1. 程式人生 > >【BZOJ2806】Cheat 【廣義後綴自動機+單調隊列優化dp+二分】

【BZOJ2806】Cheat 【廣義後綴自動機+單調隊列優化dp+二分】

都是 esp using pri 是什麽 hide 判斷 isp code

題意

有M篇標準作文組成了一個作文庫(每篇作文都是一個01的字符串),然後給出N篇作文(自然也是01字符串)。如果一個長度不小於L的串在作文庫中出現過,那麽它是熟悉的。對於某一篇作文,我們要把它分為若幹段,使得熟悉過的字符串長度>=百分之90,我們要求滿足這個條件的最小的L。

分析

這個L顯然滿足二分,然後我們要想怎麽判斷,對於當前L,這篇作文的熟悉過字符串的最長長度是什麽。我們先把作文庫建一個廣義後綴自動機,然後對於每篇作文很容易可以求出一個len[i]指的是在i位置結束的子串在作文庫中出現過的最長長度是多少。然後我們來dp。設f[i]為前綴i的最長熟悉長度。那麽f[i]=max(f[i-1],f[j]+i-j+1|(i-L>=j>=len[i]-i))。但是這個dp是要O(n^2)的。但是我們發現,這個dp是單調的,也就是說i如果是從j遞推來的,那麽i+1 就不可能從j以前遞推來。所以這個j我們用單調隊列來維護。我們單調隊列中存的是根據值f[j]-j單調的j值。

技術分享圖片
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #include <algorithm>
  5 #include <queue>
  6 
  7 using namespace std;
  8 const int maxn=3000000;
  9 char s[maxn];
 10 struct state{
 11     int len,link;
 12     int next[2];
 13
}st[2*maxn]; 14 int len[maxn]; 15 int N,M,n; 16 int last,cur,sz; 17 void init(){ 18 sz=1; 19 cur=last=0; 20 st[0].link=-1; 21 st[0].len=0; 22 } 23 void build_sam(int c){ 24 cur=sz++; 25 st[cur].len=st[last].len+1; 26 int p; 27 for(p=last;p!=-1&&st[p].next[c]==0
;p=st[p].link) 28 st[p].next[c]=cur; 29 if(p==-1) 30 st[cur].link=0; 31 else{ 32 int q=st[p].next[c]; 33 if(st[q].len==st[p].len+1){ 34 st[cur].link=q; 35 }else{ 36 int clone=sz++; 37 st[clone].len=st[p].len+1; 38 st[clone].link=st[q].link; 39 for(int i=0;i<2;i++) 40 st[clone].next[i]=st[q].next[i]; 41 for(;p!=-1&&st[p].next[c]==q;p=st[p].link) 42 st[p].next[c]=clone; 43 st[cur].link=st[q].link=clone; 44 } 45 } 46 last=cur; 47 } 48 int f[maxn]; 49 //dp[i]=max(dp[j]+i-j| i-len[i]<=j<=i-L) 50 51 bool check(int L){ 52 deque<int>q; 53 for(int i=1;i<=n;i++){ 54 f[i]=f[i-1]; 55 if(i<L)continue; 56 while(!q.empty()&&f[q.back()]-q.back()<f[i-L]-i+L) 57 q.pop_back(); 58 q.push_back(i-L); 59 while(!q.empty()&&q.front()<i-len[i]) 60 q.pop_front(); 61 if(!q.empty()) 62 f[i]=max(f[i],f[q.front()]+i-q.front()); 63 } 64 return f[n]*10>=n*9; 65 } 66 67 int main(){ 68 scanf("%d%d",&N,&M); 69 init(); 70 for(int i=1;i<=M;i++){ 71 scanf("%s",s); 72 n=strlen(s); 73 for(int j=0;j<n;j++){ 74 int c=s[j]-0; 75 build_sam(c); 76 } 77 last=0; 78 } 79 for(int q=1;q<=N;q++){ 80 scanf("%s",s+1); 81 n=strlen(s+1); 82 int u=0,Len=0; 83 for(int i=1;i<=n;i++){ 84 int c=s[i]-0; 85 while(u!=-1&&st[u].next[c]==0){ 86 u=st[u].link; 87 Len=st[u].len; 88 } 89 if(u==-1) 90 u=0,Len=0; 91 else{ 92 u=st[u].next[c]; 93 Len++; 94 } 95 len[i]=Len; 96 } 97 int l=0,r=n,ans=0; 98 while(l<=r){ 99 int mid=l+(r-l)/2; 100 if(check(mid)){ 101 ans=mid; 102 l=mid+1; 103 }else{ 104 r=mid-1; 105 } 106 } 107 printf("%d\n",ans); 108 } 109 return 0; 110 }
View Code

【BZOJ2806】Cheat 【廣義後綴自動機+單調隊列優化dp+二分】