【BZOJ4044】[CERC2014] Virus synthesis(迴文自動機上DP)
阿新 • • 發佈:2020-07-20
大致題意: 有一個字串\(S\),初始為空,你可以進行兩種操作:在\(S\)的前面或後面新增一個字元;將\(S\)複製並翻轉得到\(S'\),然後把\(S'\)接到\(S\)後面。求最少需要幾次操作才能得到給定串。
迴文自動機
關於迴文自動機可以看這篇部落格:初學回文自動機。
這裡補充一個相關定義:\(trans[x]\)。表示\(x\)所代表的迴文串中,長度小於等於\(x\)長度一半的最長迴文字尾所對應的節點。
至於如何求出\(trans[x]\),不難發現它和\(fail[x]\)定義很像,因此求法也差不多,只要在\(while\)迴圈中加上一句判斷長度是否大於\(x\)長度一半即可。
具體實現可詳見程式碼。
動態規劃
設\(f_x\)表示得到節點\(x\)表示的字串所需的最小代價。
由於迴文自動機上的字串都是迴文串,因此我們\(BFS\)遍歷迴文自動機,然後每次列舉\(k\)的後繼狀態\(x\),顯然有兩種轉移:
- 直接從\(k\)在首位各添一個字元得到,由於有迴文操作,因此這一步實際上只需要\(1\)的代價。即:
\[f_x=f_k+1 \]
- 給\(y=trans[x]\)前面添上若干字元,然後迴文操作得到\(x\)(注意這裡只需考慮在前面添上字元,是因為在後面添上字元會在上面的轉移中考慮到)。即:
\[f_x=f_{y}+1+(\frac{len[x]}2-len[y]) \]
注意由於奇迴文串無法通過迴文操作得到,在此題中沒有貢獻,因此\(BFS\)開始時我們只需將偶根加入佇列並令\(f_0=1\)即可。
最終答案就是\(\min\{f_x+(n-len[x])\}\)。
程式碼
#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define N 100000 using namespace std; int n;char s[N+5]; class FastIO { private: #define FS 100000 #define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++) #define pc(c) (C==E&&(clear(),0),*C++=c) #define D isdigit(c=tc()) int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS]; public: I FastIO() {A=B=FI,C=FO,E=FO+FS;} Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);} Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('\n');} I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;} }F; class PalindromeAutomation//迴文自動機 { private: int Nt,lst,q[N+5];struct node {int DP,L,T,F,S[4];}O[N+5]; I int Fail(RI x,CI id) {W(s[id-O[x].L-1]^s[id]) x=O[x].F;return x;}//跳Fail public: I void Init() {memset(O,0,sizeof(node)*Nt),O[O[lst=0].F=Nt=1].L=-1;}//初始化清空 I void Ins(CI id)//插入新元素 { #define GV(c) (c=='A'?0:(c=='T'?1:(c=='G'?2:3))) RI x=GV(s[id]),t=Fail(lst,id),o;if(!O[t].S[x])//如果沒有該兒子 { O[o=++Nt].L=O[t].L+2,O[o].F=O[Fail(O[t].F,id)].S[x],O[t].S[x]=o;//計算資訊 if(O[o].L<=2) O[o].T=O[o].F;else//求trans { RI k=O[t].T;W(s[id-O[k].L-1]^s[id]||(O[k].L+2<<1)>O[o].L) k=O[k].F;//注意判斷長度是否大於一半 O[o].T=O[k].S[x];//記錄trans } }lst=O[t].S[x];//更新lst } I int Work()//DP { for(RI i=1;i<=Nt;++i) O[i].DP=1e9;//初始化 RI i,k,x,y,ans=n,H=1,T=0;O[q[++T]=0].DP=1;W(H<=T)//BFS { for(k=q[H++],i=0;i^4;++i) (x=O[k].S[i])&&//列舉後繼 ( y=O[x].T,O[x].DP=min(O[k].DP+1,O[y].DP+1+(O[x].L>>1)-O[y].L),//DP轉移 ans=min(ans,O[x].DP+n-O[x].L),q[++T]=x//統計答案,加入BFS佇列 ); }return ans; } }P; int main() { RI Tt,i;scanf("%d",&Tt);W(Tt--) { for(scanf("%s",s+1),n=strlen(s+1),P.Init(),i=1;i<=n;++i) P.Ins(i);//建迴文自動機 printf("%d\n",P.Work());//輸出答案 }return 0; }