BZOJ 4044 Virus synthesis (回文自动机+dp)
题目大意:
你可以在一个串的开头或者末尾加入一个字符,或者把当前整个串$reverse$,然后接在前面或者后面,求达到目标串需要的最少操作次数
对目标串建出$PAM$
定义$dp[x]$表示当前在回文树的x节点,拼凑出这个节点表示的回文串所需要的最小操作次数
$fa_{x}$表示沿着树边指向它的父亲,$pre_{x}$表示它的$fail$指针
如果它是奇回文串,一定不能被翻转得到,所以开头结尾各需要加上一个字符,$dp[x]=dp[fa_{x}]+2$
如果它是偶回文串,可以被翻转得到
1.要么是在上次翻转之前先添加一个字符,再翻转得到,$dp[x]=dp[fa_{x}]+1$
2.要么是在这次进行翻转,需要保证被翻转的串长度不大于$dep[x]/2$
可以在回文树上倍增跳$pre$,找到$x$的一个长度不大于$dep[x]/2$的回文后缀所在节点$y$
只需要找后缀的情况即可,其他的情况会被合并到情况一里
剩下的部分也要被暴力地填上,那么$dp[x]=dp[y]+dep[x]/2-dep[y]+1$
1 #include <cmath> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #define N1 100100 7 #define S1 (N1<<1) 8 #define ll long long 9 #define uint unsigned int 10 #define rint register int 11 #define dd double 12 #define il inline 13 #define inf 0x3f3f3f3f 14 using namespace std; 15 16 int len,T; 17 inline int idx(char x) 18 { 19 if(x=='A') return 0; 20 if(x=='C') return 1; 21 if(x=='G') return 2; 22 if(x=='T') return 3; 23 } 24 char str[N1]; 25 int lg[N1]; 26 namespace PAM{ 27 int trs[N1][4],pre[N1],dep[N1],fa[N1],dp[N1],la,tot; 28 int ff[N1][17]; 29 void init() 30 { 31 tot++; 32 memset(trs,0,tot*4*4),memset(pre,0,tot*4); 33 memset(ff,0,tot*4*16),memset(fa,0,tot*4); 34 memset(dp,0,tot*4),memset(dep,0,tot*4); 35 la=tot=1,dep[1]=-1;pre[0]=pre[1]=1; 36 } 37 int chk(char *str,int i,int p){return str[i-dep[p]-1]!=str[i]?1:0;} 38 void insert(char *str,int i) 39 { 40 int p=la,np,fp,c=idx(str[i]); 41 while(chk(str,i,p)) p=pre[p]; 42 if(!trs[p][c]) 43 { 44 np=++tot; 45 dep[np]=dep[p]+2; 46 fp=pre[p]; 47 while(chk(str,i,fp)) fp=pre[fp]; 48 pre[np]=trs[fp][c]; 49 trs[p][c]=np; 50 fa[np]=p; 51 } 52 la=trs[p][c]; 53 } 54 int solve() 55 { 56 int i,j,x,ans=len,de; 57 ff[0][0]=ff[0][1]=ff[1][0]=ff[1][1]=0; 58 for(i=2;i<=tot;i++) ff[i][0]=i,ff[i][1]=pre[i]; 59 for(j=2;j<=16;j++) 60 for(i=2;i<=tot;i++) 61 ff[i][j]=ff[ ff[i][j-1] ][j-1]; 62 dp[0]=1,dp[1]=-1; 63 for(i=2;i<=tot;i++) 64 { 65 x=i; 66 dp[i]=dp[fa[i]]+((dep[i]&1)?2:1); 67 if((dep[i]&1)) continue; 68 for(j=lg[dep[i]];j>=0;j--) 69 if(dep[ff[x][j]]>=dep[i]/2) x=ff[x][j]; 70 if(dep[x]>dep[i]/2) x=pre[x]; 71 dp[i]=min(dp[i],dp[x]+1+dep[i]/2-dep[x]); 72 ans=min(ans,len-dep[i]+dp[i]); 73 } 74 return ans; 75 } 76 }; 77 78 int main() 79 { 80 scanf("%d",&T); 81 while(T--) 82 { 83 scanf("%s",str+1); 84 len=strlen(str+1); 85 int i; 86 for(lg[1]=0,i=2;i<=len;i++) lg[i]=lg[i>>1]+1; 87 PAM::init(); 88 for(i=1;i<=len;i++) PAM::insert(str,i); 89 printf("%d\n",PAM::solve()); 90 } 91 return 0; 92 }