zoukankan      html  css  js  c++  java
  • [CERC2014]Virus synthesis

    题目描述

    题解

    貌似只有回文树的解法。

    回文树

    一种自动机,可以识别所有的回文串。

    性质:一个串中本质不同的回文串最多有n个。

    和其他自动机一样,它记录了一个ch数组,一个fail数组,fail数组在这里的指向是这个串的最长回文后缀。

    在回文树中,每个节点都存了一个回文串,为了区分奇数串和偶数串,1->奇数0->偶数。

    为了防止边界爆炸,len[1]=-1,fail[0]=1。

    构造方法:仍是增量构造,我们可以维护一个变变量last,和后缀自动机一样,表示上一次的节点。

    然后我们可以判断一下s[i-len[x]-1]==s[i]如果不满足,就跳last的fail,知道找到最长的last的后缀满足接上当前节点后是一个回文串。

    然后我们新开节点cnt,len[cnt]=len[last]+2。

    +2是因为我在last前后各添加了一个字符,所以要+2,。

    然后就要连fail了,找到fail[last],和上面一样跳,然后直接连。

    例题

    APIO2014回文串,直接建出回文树,统计一下。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define N 300002
    using namespace std;
    typedef long long ll;
    ll ans;
    char s[N];
    int n,tong[N],fail[N],len[N],ch[N][26],cnt,last;
    int main(){
        scanf("%s",s+1);n=strlen(s+1);
        len[1]=-1;fail[0]=1;s[0]='&';cnt=last=1;
        for(int i=1;i<=n;++i){
            int x=s[i]-'a';
            while(s[i-len[last]-1]!=s[i])last=fail[last];
            if(!ch[last][x]){
                len[++cnt]=len[last]+2;
                int j=fail[last];
                while(s[i-len[j]-1]!=s[i])j=fail[j];
                fail[cnt]=ch[j][x];
                ch[last][x]=cnt;
            }
            last=ch[last][x];++tong[last];
        } 
        for(int i=cnt;i>=2;--i){
            tong[fail[i]]+=tong[i];
          ans=max(ans,1ll*tong[i]*len[i]);
        }
        cout<<ans;
        return 0;
    }
    View Code

    SHOI2011双倍回文:对每一个节点求一个father,表示小于等于这个串一半的最长回文后缀,求法和求fail类似,然后统计一下(虽然我用的暴力加剪枝)。

    #include<iostream>
    #include<cstdio>
    #define N 500002
    using namespace std;
    int len[N],last,cnt,fail[N],ch[N][26],n,ans;
    char s[N];
    int main(){
        scanf("%d%s",&n,s+1);
        last=cnt=1;len[1]=-1;fail[0]=1;
        for(int i=1;i<=n;++i){
            while(s[i-len[last]-1]!=s[i])last=fail[last];
            if(!ch[last][s[i]-'a']){
                len[++cnt]=len[last]+2;
                int x=fail[last];
                while(s[i-len[x]-1]!=s[i])x=fail[x];
                fail[cnt]=ch[x][s[i]-'a'];ch[last][s[i]-'a']=cnt;
            }
            last=ch[last][s[i]-'a'];
        }
        for(int i=cnt;i>=1;--i){   ///!!!
            if(len[i]%4)continue;
            if(len[i]<=ans)continue;
            int x=i;
            while(len[x]*2>len[i])x=fail[x];
            if(len[x]*2==len[i])ans=max(ans,len[i]);
        }
        cout<<ans;
        return 0;
    }
    View Code

    然后看这道题。

    先搞出来回文树。

    考虑令dp[i]表示搞出来i这个回文串的最小代价、

    然后我们在0的子树上转移,因为奇回文串不满足翻转性质。

    dp[v]=dp[u]+1因为是0的子树,它是偶回文串,已经被翻过了,所以我们假装在翻之前就把这个字符填上去了。

    dp[v]=dp[fa]+len[v]/2+1,我们在它的father上搞点事情,因为它的长度小于一半,所以我们把它补到一半后翻一下就好了。

    那为什么不考虑前面的往后面翻的情况呢?

    因为这两种情况代价相等。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #define N 100002
    using namespace std;
    queue<int>q;
    int ch[N][4],len[N],fail[N],n,t,last,trans[200],cnt,fa[N],dp[N],ans;
    char s[N];
    int main(){
        trans['A']=0;trans['T']=1;trans['C']=2;trans['G']=3;
        scanf("%d",&t);
        while(t--){    
            scanf("%s",s+1);n=strlen(s+1);
            last=cnt=1;fail[0]=1;len[1]=-1;s[0]='#';
            for(int i=1;i<=n;++i){
                int y=trans[s[i]];
                while(s[i-len[last]-1]!=s[i])last=fail[last];
                if(!ch[last][y]){
                    int x=fail[last];
                    len[++cnt]=len[last]+2; 
                    while(s[i-len[x]-1]!=s[i])x=fail[x];
                    fail[cnt]=ch[x][y];ch[last][y]=cnt;
                    if(len[cnt]>2){
                    int tmp=fa[last];
                    while(s[i-len[tmp]-1]!=s[i]||(len[tmp]+2)*2>len[cnt])tmp=fail[tmp];
                    fa[cnt]=ch[tmp][y];
                    }else fa[cnt]=fail[cnt];
                }
                last=ch[last][y];
            }
            ans=n;
            for(int i=2;i<=cnt;++i)dp[i]=len[i];
            dp[0]=1;dp[1]=0;
            q.push(0);
            while(!q.empty()){
                int u=q.front();q.pop();
                for(int i=0;i<4;++i)if(ch[u][i]){
                    int v=ch[u][i]; 
                    dp[v]=min(dp[v],dp[u]+1);
                    int o=fa[v];
                    dp[v]=min(dp[v],dp[o]+len[v]/2-len[o]+1);
                    ans=min(ans,dp[v]+n-len[v]);
                    q.push(v);
                }
            }
        //    for(int i=1;i<=cnt;++i)cout<<dp[i]<<" ";cout<<endl;
            printf("%d
    ",ans);
            for(int i=0;i<=cnt;++i){
             fail[i]=0,fa[i]=0,len[i]=0;
             for(int j=0;j<4;++j)ch[i][j]=0;
            }
        } 
        return 0;
    } 
  • 相关阅读:
    JavaScript function (简单总结)
    JavaScript 数组 (简单总结)
    yum update 和 yum upgrate 区别
    git clone警告,提示Warning:Permission denied (publickey)
    ''退格符号笔记
    MySQL Workbench导出Model提示['ERROR 1064 (42000): You have an error in your SQL syntax....syntax to use near 'VISIBLE']
    《Python编程从入门到实践》--- 学习过程笔记(3)列表
    《Python编程从入门到实践》--- 学习过程笔记(2)变量和简单数据类型
    Windows+MyEclipse+MySQL【连接数据库报错caching_sha2_password】
    测试 | 让每一粒尘埃有的放矢
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10174014.html
Copyright © 2011-2022 走看看