P4762 [CERC2014]Virus synthesis

题意

真是道回文自动机好题。

首先考虑答案必定是一个回文串+剩余部分的形式,因此可以建出回文自动机,之后考虑每个长度为偶数的回文串。

对于一个长度为偶数的回文串,设它在回文自动机上对应的节点为\(x\),我们对于每个\(x\)求出\(trans_x\)表示x的最长后缀回文串,满足\(len_{trans_x}\leqslant len_x/2\)

之后设\(f_x\)表示\(x\)拼成\(x\)这个串的最小代价,我们从\(0\)(偶根)出发进行\(bfs\),中途计算\(f_x\)

对于\(f_x\)
初值肯定是自身长度\(f_x=len_x\)
如果存在一条边\((x,y)\),那么\(f_y=f_x+1\),因为我们可以在拼\(x\)时还没进行\(2\)操作时向\(x\)后面填一个字符,使其进行\(2\)操作后变为\(y\)
同时\(f_x=min(f_x,f_{trans_x}+1+lem_x/2-len_{trans_x})\),即我们可以从\(trans_x\)变过来。

对于每个\(x\),它对答案的贡献是\(n-len_x+f_x\)\(n\)是字符串长度。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int inf=1e9;
int T,n,tot,last,ans;
int fail[maxn],len[maxn],trans[maxn],f[maxn];
int ch[maxn][5];
char s[maxn];
inline int change(char c)
{
    if(c=='A')return 1;
    if(c=='G')return 2;
    if(c=='C')return 3;
    if(c=='T')return 4;
    return 2333;
}
inline void init()
{
    for(int i=0;i<=tot;i++)
        for(int j=1;j<=4;j++)
            ch[i][j]=0;
    fail[0]=1;len[1]=-1;
    tot=1;last=0;
}
inline int getfail(int x,int pos)
{
    while(s[pos-len[x]-1]!=s[pos])x=fail[x];
    return x;
}
inline void add(int c,int pos)
{
    int p=getfail(last,pos);
    if(!ch[p][c])
    {
        int q=++tot,tmp;len[q]=len[p]+2;
        tmp=getfail(fail[p],pos);
        fail[q]=ch[tmp][c];ch[p][c]=q;
        if(len[q]<=2)trans[q]=fail[q];
        else  
        {
            tmp=trans[p];
            while(s[pos-len[tmp]-1]!=s[pos]||((len[tmp]+2)<<1)>len[q])tmp=fail[tmp];
            trans[q]=ch[tmp][c];
        }
    }
    last=ch[p][c];
}
inline void solve()
{
    queue<int>q;
    for(int i=2;i<=tot;i++)f[i]=len[i];
    for(int i=1;i<=4;i++)if(ch[0][i])q.push(ch[0][i]);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        f[x]=min(f[x],f[trans[x]]+1+len[x]/2-len[trans[x]]);
        ans=min(ans,n-len[x]+f[x]);
        for(int i=1;i<=4;i++)
        {
            if(!ch[x][i])continue;
            int y=ch[x][i];
            f[y]=min(f[y],f[x]+1);
            q.push(y);
        }
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",s+1);n=strlen(s+1);
        s[0]='#';
        init();
        for(int i=1;i<=n;i++)add(change(s[i]),i);
        ans=n;solve();
        printf("%d\n",ans);
    }
    return 0;   
}

猜你喜欢

转载自www.cnblogs.com/nofind/p/12070004.html