【BZOJ2806】【CTSC2012】Cheat - 廣義後綴自動機+單調隊列優化DP

題意:

Description

Input

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

Output

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

HINT

輸入文件不超過1100000字節優化

注意:題目有改動,可識別的長度不小於90%便可,而不是大於90% spa

題解:

好題!code

先對標準做文庫建出廣義SAM,把每一個做文串在SAM上匹配,求出每一位以前最長出現過子串長度$s[i]$;orm

顯然沒有什麼好辦法直接求L,考慮二分答案判斷是否可行;blog

設當前二分值爲$mid$,$f[i]$表示前$i$位最長的熟悉長度,則易得DP方程:$f[i]=max\{f[j]+i-j\}$,決策區間就是$[i-s[i],i-mid]$;隊列

顯然$i-s[i]$單調不降,因此能夠用單調隊列優化到$O(n)$ip

最後判一下比例是否大於$90\%$便可。string

代碼:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 using namespace std; 10 typedef long long ll; 11 typedef double db; 12 int n,m,l,r,ans,len,tot=1,last,rt=1,son[2200001][2],fa[2200001],mx[2200001],s[1100001],q[1100001],f[1100001]; 13 char st[1100001]; 14 void extend(int ch){ 15     int p=last,np=++tot; 16     mx[np]=mx[p]+1; 17     for(;p&&!son[p][ch];p=fa[p])son[p][ch]=np; 18     if(!p)fa[np]=rt; 19     else{ 20         int q=son[p][ch]; 21         if(mx[q]==mx[p]+1)fa[np]=q; 22         else{ 23             int nq=++tot; 24             mx[nq]=mx[p]+1; 25             memcpy(son[nq],son[q],sizeof(son[q])); 26             fa[nq]=fa[q]; 27             fa[q]=fa[np]=nq; 28             for(;p&&son[p][ch]==q;p=fa[p])son[p][ch]=nq; 29  } 30  } 31     last=np; 32 } 33 void pre(char *st,int len){ 34     int nw=rt,tmp=0; 35     for(int i=1;i<=len;i++){ 36         if(son[nw][st[i]-'0']){ 37             nw=son[nw][st[i]-'0']; 38             tmp++; 39         }else{ 40             for(;nw&&!son[nw][st[i]-'0'];nw=fa[nw]); 41             if(!nw){ 42                 nw=rt; 43                 tmp=0; 44             }else{ 45                 tmp=mx[nw]+1; 46                 nw=son[nw][st[i]-'0']; 47  } 48  } 49         s[i]=tmp; 50  } 51 } 52 bool check(int k){ 53     int L=1,R=0; 54     for(int i=1;i<=len;i++){ 55         f[i]=f[i-1]; 56         if(i<k)continue; 57         for(;L<=R&&f[q[R]]-q[R]<f[i-k]-i+k;R--); 58         q[++R]=i-k; 59         for(;L<=R&&q[L]<i-s[i];L++); 60         if(L<=R)f[i]=max(f[i],f[q[L]]-q[L]+i); 61  } 62     return f[len]*10>=len*9; 63 } 64 int main(){ 65     scanf("%d%d",&n,&m); 66     for(int i=1;i<=m;i++){ 67         scanf("%s",st+1); 68         len=strlen(st+1); 69         last=rt; 70         for(int j=1;j<=len;j++){ 71             extend(st[j]-'0'); 72  } 73  } 74     for(int i=1;i<=n;i++){ 75         scanf("%s",st+1); 76         len=strlen(st+1); 77  pre(st,len); 78         l=ans=0,r=len; 79         while(l<=r){ 80             int mid=(l+r)/2; 81             if(check(mid))ans=mid,l=mid+1; 82             else r=mid-1; 83  } 84         printf("%d\n",ans); 85  } 86     return 0; 87 }
相關文章
相關標籤/搜索