【bzoj2806】 Ctsc2012—Cheat
http://www.lydsy.com/JudgeOnline/problem.php?id=2806?(題目鏈接)
題意
給出M個字符串組成“標準庫”。定義L表示將一個字符串分成若干段,每一段的長度不小于L,其中是在標準庫中任一字符串的子串的字符“段”的長度之和不小于原字符串長度之和的90%。N個詢問,每個給出一個字符串,求其滿足條件的最大的L。
Solution
對于每一個詢問,我們在線做,話說離線怎么做,整體二分嗎→_→
很顯然,這應該是要二分答案,考慮怎么check。我們想到了dp:${f_i}$表示后綴${i}$的最長覆蓋長度。
$${f_{i}=Max\{f_{i+1},f_j+j-i\},i+L_0<=j<=R_i+i}$$
其中${L_0}$表示當前二分的答案,${R_i}$表示從第${i}$位開始能夠匹配到的最長的連續段長度。注意這個要倒著做→_→
然而這個dp是${O(n^2)}$的,我們還需要優化,加上一個括號:
$${f_i=Max\{f_{i+1},(f_j+j)-i\},i+L_0<=j<=R_i+i}$$
于是${f_j+j}$就只與${j}$有關了,我們考慮單調隊列。如果隊首因為它的位置${>=R_i+i}$而被踢出了隊列,那么它必然不會因為后面串的${R_i}$長度增大而被加回來,因為每往后面挪一格長度只可能+1,然后又因為i會-1,所以就是不變的。
那么我們就可以${O(nlogn)}$的求解每一個詢問了。
那么只剩下一個問題,${R_i}$怎么求。考慮后綴數組。將所有串接在一起,求一個后綴數組,然后求出height,那么如果一個后綴是詢問串的后綴,與其最近的“標準庫”中的后綴的height就是它的${R_i}$。然后倍增構后綴數組就TLE飛了→_→,等下補一發后綴自動機的。
UPD 2017.1.19:
這幾天一直在考試,拖到了現在。原來就是把“標準庫”中的串用分隔符接起來構后綴自動機,之后每一個詢問串在上面跑匹配就可以輕松的求出${R_i}$了,我還寫了發后綴自動機構后綴數組,然而分隔符太大了,開不下→_→。
因為之前的dp是倒著求的,所以這里的后綴自動機和匹配都是倒著來的→_→,懶得再去改dp和二分答案了。。
細節
構造后綴數組時桶的大小要注意;求${R_i}$的時候要想清楚→_→。
后綴自動機匹配的時候長度要隨著匹配失敗跳parent樹的移動而改變。
代碼(后綴數組)
// bzoj2806 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<vector> #include<cstdio> #include<cmath> #include<ctime> #define LL long long #define inf 1<<30 #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std;const int maxn=2000010; int f[maxn],S[maxn],a[maxn],vis[maxn],q[maxn],pl[maxn],pr[maxn],R[maxn]; int rank[maxn],sa[maxn],height[maxn]; char s[maxn]; int n,m,ans;namespace Suffix {int wa[maxn],wb[maxn],ww[maxn];bool cmp(int *r,int a,int b,int l) {return r[a]==r[b] && r[a+l]==r[b+l];}void da(int *r,int *sa,int n,int m) {int i,j,p,*x=wa,*y=wb;for (i=0;i<=m;i++) ww[i]=0;for (i=1;i<=n;i++) ww[x[i]=r[i]]++;for (i=1;i<=m;i++) ww[i]+=ww[i-1];for (i=n;i>=1;i--) sa[ww[x[i]]--]=i;for (p=0,j=1;p<n;j*=2,m=p) {for (p=0,i=n-j+1;i<=n;i++) y[++p]=i;for (i=1;i<=n;i++) if (sa[i]>j) y[++p]=sa[i]-j;for (i=0;i<=m;i++) ww[i]=0;for (i=1;i<=n;i++) ww[x[y[i]]]++;for (i=1;i<=m;i++) ww[i]+=ww[i-1];for (i=n;i>=1;i--) sa[ww[x[y[i]]]--]=y[i];for (swap(x,y),p=x[sa[1]]=1,i=2;i<=n;i++)x[sa[i]]=cmp(y,sa[i-1],sa[i],j) ? p : ++p;}}void calheight(int *r,int *sa,int n) {for (int i=1;i<=n;i++) rank[sa[i]]=i;for (int k=0,i=1;i<=n;i++) {if (k) k--;int j=sa[rank[i]-1];while (r[i+k]==r[j+k]) k++;height[rank[i]]=k;}} } using namespace Suffix;bool dp(int x,int L0) {for (int i=pl[x];i<=pr[x];i++) f[i]=0;int l=1,r=1;q[1]=pr[x]+1;for (int i=pr[x]-L0+1;i>=pl[x];i--) {while (l<=r && q[l]>i+R[i]) l++;f[i]=f[i+1];if (l<=r) f[i]=max(f[i],f[q[l]]+q[l]-i);if (i+L0-1<=pr[x]) {while (l<=r && f[q[r]]+q[r]<f[i+L0-1]+i+L0-1) r--;q[++r]=i+L0-1;}}return 10*f[pl[x]]>=(pr[x]-pl[x]+1)*9; } int main() {scanf("%d%d",&n,&m);int len=0;S[0]=inf;for (int i=1;i<=m;i++) {scanf("%s",s+1);for (int j=1;j<=strlen(s+1);j++) S[++len]=s[j]-'0';S[++len]=i+2;}for (int i=1;i<=n;i++) {scanf("%s",s+1);int tmp=strlen(s+1);pl[i]=len+1;for (int j=1;j<=tmp;j++) S[++len]=s[j]-'0',vis[len]=1;pr[i]=len;S[++len]=i+m+2;}da(S,sa,len,1000000);calheight(S,sa,len);for (int l=inf,i=2;i<=len;i++) {if (i==2) while (vis[sa[i-1]] && vis[sa[i]] && i<=len) i++;if (!vis[sa[i]]) l=inf;else {l=min(l,height[i]);R[sa[i]]=max(R[sa[i]],l);}}for (int l=inf,i=len-1;i>=1;i--) {if (i==len-1) while (vis[sa[i+1]] && vis[sa[i]] && i>=1) i--;if (!vis[sa[i]]) l=inf;else {l=min(l,height[i+1]);R[sa[i]]=max(R[sa[i]],l);}}for (int i=1;i<=n;i++) {int l=0,r=pr[i]-pl[i]+1,ans=0;while (l<=r) {int mid=(l+r)>>1;if (dp(i,mid)) ans=mid,l=mid+1;else r=mid-1;}printf("%d\n",ans);}return 0; }?代碼(后綴自動機)
// bzoj2806 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<vector> #include<cstdio> #include<cmath> #include<ctime> #define LL long long #define inf 1<<30 #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std;const int maxn=2000010; int f[maxn],q[maxn],R[maxn]; char s[maxn]; int n,m,N,ans;namespace SAM {int par[maxn<<1],len[maxn<<1],pos[maxn<<1],ch[maxn>>1][10];int last,Dargen,sz;void Extend(int c) {int np=++sz,p=last;last=np;len[np]=len[p]+1;for (;p && !ch[p][c];p=par[p]) ch[p][c]=np;if (!p) par[np]=Dargen;else {int q=ch[p][c];if (len[p]+1==len[q]) par[np]=q;else {int nq=++sz;len[nq]=len[p]+1;memcpy(ch[nq],ch[q],sizeof(ch[q]));par[nq]=par[q];par[np]=par[q]=nq;for (;p && ch[p][c]==q;p=par[p]) ch[p][c]=nq;}}}void match() {int ll=0;for (int p=Dargen,i=n;i>=1;i--) {while (p>1 && !ch[p][s[i]-'0']) p=par[p],ll=len[p];if (ch[p][s[i]-'0']) p=ch[p][s[i]-'0'],ll++;else ll=0;R[i]=ll;}} } using namespace SAM;bool dp(int x,int L0) {for (int i=1;i<=n+1;i++) f[i]=0;int l=1,r=1;q[1]=n+1;for (int i=n-L0+1;i>=1;i--) {while (l<=r && q[l]>i+R[i]) l++;f[i]=f[i+1];if (l<=r) f[i]=max(f[i],f[q[l]]+q[l]-i);if (i+L0-1<=n) {while (l<=r && f[q[r]]+q[r]<f[i+L0-1]+i+L0-1) r--;q[++r]=i+L0-1;}}return 10*f[1]>=n*9; } int main() {scanf("%d%d",&N,&m);SAM::sz=SAM::Dargen=SAM::last=1;for (int i=1;i<=m;i++) {scanf("%s",s+1);for (int i=strlen(s+1);i>=1;i--) Extend(s[i]-'0');Extend(2);}for (int i=1;i<=N;i++) {scanf("%s",s+1);n=strlen(s+1);match();int l=0,r=n,ans=0;while (l<=r) {int mid=(l+r)>>1;if (dp(i,mid)) ans=mid,l=mid+1;else r=mid-1;}printf("%d\n",ans);}return 0; }?
轉載于:https://www.cnblogs.com/MashiroSky/p/6294884.html
總結
以上是生活随笔為你收集整理的【bzoj2806】 Ctsc2012—Cheat的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kafka Failed to send
- 下一篇: 影像图矢量化水系