BZOJ4044: [Cerc2014] Virus synthesis

138 篇文章 0 订阅
3 篇文章 0 订阅

考虑最后一次翻倍操作,操作后就是一个回文串,然后不断向两边添加字符得到原串
我们建出回文自动机,考虑对每个回文串dp出建出他需要的最少步数
若g[i]表示构建出回文串i的最少步数
为了方便,我设的dp状态是f[i]=g[i]-i

对于一个奇数回文串A,令B为A去掉尾部字符的串,C为A去掉两端字符得到的回文串,
若B是回文串,D=B,否则D=C
那么考虑我们构建串A最后一次翻倍操作后的添加操作,如果全部在头部添加
f [ A ] = f [ f a i l [ A ] ] f[A]=f[fail[A]] f[A]=f[fail[A]]
如果有在尾部添加的操作
f [ A ] = f [ D ] f[A]=f[D] f[A]=f[D]
对于每个奇数回文串A,D可以在构建回文自动机的时候顺便处理出来

对于一个偶数回文串E,令F为E去掉两端字符的回文串,G为p从E开始,不断跳fail[p]直到len[p]<=len[E]
可以用反证法证明E最后一次操作一定是翻倍,考虑倒数第二次翻倍操作之后的操作,如果全部是在头部添加
f [ E ] = f [ G ] − l e n [ E ] / 2 + 1 f[E]=f[G]-len[E]/2+1 f[E]=f[G]len[E]/2+1
如果有在尾部添加的
f [ E ] = f [ F ] − 1 f[E]=f[F]-1 f[E]=f[F]1
注意判F,G不存在的情况
这个F可以也在构建回文自动机的时候处理出来,G的话要构建完自动机后把fail树建出来,dfs一遍fail树求出每个点的G

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
  
const int maxn = 210000;
const int maxc = 4;
  
int n;
char str[maxn]; int S[maxn];
  
struct Tree
{
    int son[maxn][maxc],fail[maxn],len[maxn],tot,last;
    int pre[maxn];
    void init()
    {
        tot=1; last=0;
        memset(son[0],0,sizeof son[0]);
        memset(son[1],0,sizeof son[1]);
        fail[0]=1,fail[1]=0;
        len[0]=0,len[1]=-1;
    }
    int newnode(int l)
    {
        ++tot;
        memset(son[tot],0,sizeof son[tot]);
        len[tot]=l,fail[tot]=0;
        pre[tot]=0;
        return tot;
    }
    void extend(const int i)
    {
        int p=last,w=S[i],lp=0,np;
        while(S[i-1-len[p]]!=w) lp=p,p=fail[p];
        if(!son[p][w])
        {
            np=newnode(len[p]+2);
            if(len[np]&1) pre[np]=len[lp]+1==len[np]?lp:p;
            else pre[np]=p;
            int t=fail[p];
            while(S[i-1-len[t]]!=w) t=fail[t];
            fail[np]=son[t][w];
            son[p][w]=np;
        }
        else np=son[p][w];
        last=np;
    }
}Tr;
  
struct edge{int y,nex;}a[maxn]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}
  
int mark[maxn],nex[maxn];
void dfs(const int x,int la)
{
    if(x>1)
    {
        while(Tr.len[nex[la]]*2<=Tr.len[x]) la=nex[la];
        if(Tr.len[x]%2==0) mark[x]=la;
    }
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
        nex[x]=y,dfs(y,la);
}
  
int f[maxn];
  
int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);
      
    int T;scanf("%d",&T);
    while(T--)
    {
        Tr.init();
          
        scanf("%s",str); n=strlen(str);
        S[0]=-1;
        for(int i=0;i<n;i++)
        {
            if(str[i]=='A') S[i+1]=0;
            else if(str[i]=='G') S[i+1]=1;
            else if(str[i]=='C') S[i+1]=2;
            else S[i+1]=3;
        }
        for(int i=1;i<=n;i++) 
            Tr.extend(i);
          
        len=0; for(int i=0;i<=Tr.tot;i++) fir[i]=mark[i]=0;
        for(int i=0;i<=Tr.tot;i++) if(i!=1) ins(Tr.fail[i],i);
        dfs(1,1);
          
        int ans=0;
        for(int i=2;i<=Tr.tot;i++)
        {
            if(Tr.len[i]&1) f[i]=min(f[Tr.fail[i]],f[Tr.pre[i]]);
            else
                f[i]=min(f[Tr.pre[i]]-(Tr.len[Tr.pre[i]]>0),!mark[i]?0:(f[mark[i]]-Tr.len[i]/2+1));
            ans=min(ans,f[i]);
        }
        printf("%d\n",n+ans);
    }
      
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值