[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\n",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;
} 

猜你喜欢

转载自www.cnblogs.com/ZH-comld/p/10174014.html
今日推荐