设f[x]表示得到x这个回文串的最小步数,则ans=min(n-len[x]+f[x])
边界条件f[长度为0的偶回文串]=1
因为翻转只会得到偶回文串,所以f[奇回文串]=该串的长度
对于一个偶回文串x,设y为x去掉首尾得到的串,有f[x]=f[y]+1
设y为长度不超过x的一半的x的最长回文后缀,有f[x]=len[x]/2-len[y]+f[y]+1
两种情况取个最小值即可。
对于状态的表示以及最长回文后缀的询问,用回文树支持操作即可。时间复杂度$O(n)$。
#include<cstdio> #include<cstring> const int N=100010,S=4; int T,n,i,x,y,ans,all,son[N][S],fail[N],trans[N],f[N],len[N],text[N],last,tot,q[N],h,t;char s[N]; inline int newnode(int l){ for(int i=0;i<S;i++)son[tot][i]=0; len[tot]=l; return tot++; } inline void init(){ last=tot=all=0; newnode(0),newnode(-1); text[0]=-1,fail[0]=1; } inline int getfail(int x){ while(text[all-len[x]-1]!=text[all])x=fail[x]; return x; } inline void add(int w){ text[++all]=w; int x=getfail(last); if(!son[x][w]){ int y=newnode(len[x]+2); fail[y]=son[getfail(fail[x])][w]; if(len[y]<=2)trans[y]=fail[y];else{ int z=trans[x]; while(text[all-len[z]-1]!=text[all]||(len[z]+2)*2>len[y])z=fail[z]; trans[y]=son[z][w]; } son[x][w]=y; } last=son[x][w]; } inline int hash(char c){ if(c=='A')return 0; if(c=='T')return 1; if(c=='C')return 2; return 3; } inline void up(int&a,int b){if(a>b)a=b;} int main(){ for(scanf("%d",&T);T--;printf("%d ",ans)){ scanf("%s",s+1),ans=n=std::strlen(s+1); for(init(),i=1;i<=n;i++)add(hash(s[i])); for(i=2;i<tot;i++)if(len[i]&1)f[i]=len[i]; f[q[h=t=0]=0]=1; while(h<=t)for(x=q[h++],i=0;i<4;i++)if(son[x][i]){ f[y=son[x][i]]=f[x]+1; up(f[y],len[y]/2-len[trans[y]]+f[trans[y]]+1); up(ans,n-len[y]+f[y]); q[++t]=y; } } return 0; }