G - Virus synthesis(回文树+dp)

题意

有AGTC四种字符,一开始有一个空串,每次操作,可以在首或尾加任意个字符,或者将已有字符镜面复制(左右两种复制方法),要求最少的操作步数使得得到给出的字符串

Solution:

我们要知道我们要求的是什么,是每一给回文串的所需要的最少的构造操作加上n-这个串的长度

然后就考虑如何求前者

题解:https://blog.csdn.net/nudt_spy/article/details/100061078

Code;

#include<bits/stdc++.h>
#define N 100010
#define INF 0x3f3f3f3f
#define eps 1e-6
#define pi 3.141592653589793
#define mod 777777777
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<"      :   "<<x<<endl
#define mem(x,y) memset(x,0,sizeof(int)*(y+3))
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef  pair<int,int> pp;
char s[N];
int ans;
int tot;

struct PAM {
    int next[N][4] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    int fail[N] ;//fail指针,失配后跳转到fail指针指向的节点
    int cnt[N]; //表示节点i表示的回文串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)
    int num[N] ; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数(包括本身)。
    int len[N] ;//len[i]表示节点i表示的回文串的长度
    int s[N] ;//存放添加的字符
    int half[N];
    int dp[N];
    int last ;//指向上一个字符所在的节点,方便下一次add
    int n ;//字符数组指针
    int p ;//节点指针
    //r为结尾的回文串的长度一定可以分成logn段等差数列
    inline int newnode ( int l ) {//新建节点
        for ( int i = 0 ; i < 4 ; ++ i ) next[p][i] = 0 ;
        cnt[p]= 0 ;
        num[p] = 0 ;
        len[p] = l ;
        return p ++ ;
    }
    inline void init () {//初始化
        p = 0 ;
        newnode (  0 ) ;
        newnode ( -1 ) ;
        last = 0 ;
        n = 0 ;
        s[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
        fail[0] = 1 ;
        half[0]=half[1]=1;
    }
    inline int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
        while ( s[n - len[x] - 1] != s[n] ) x = fail[x] ;
        return x ;
    }
    inline void add ( int c ) {
        // c -= ‘a‘ ;
        s[++ n] = c ;
        int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
        if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode ( len[cur] + 2 ) ;//新建节点
            fail[now] = next[get_fail ( fail[cur] )][c] ;
            next[cur][c] = now ;
            if (len[now]==1) half[now]=0;else{
                int pos=half[cur];
                while ( s[n - len[pos] - 1] != s[n]||len[pos]+2>len[now]/2 ) pos = fail[pos] ;
                half[now]=next[pos][c];
            }
            if (len[now]&1)
                dp[now]=dp[fail[now]]+len[now]-len[fail[now]];
            else
                if (len[now]<=2) dp[now]=len[now];
            else
                dp[now]=min(dp[cur]+1,dp[half[now]]+len[now]/2-len[half[now]]+1);
        }
        last = next[cur][c] ;
        ans=min(ans,dp[last]+tot-len[last]);
    }
}A;

int main(int argc, char const *argv[])
{
    int T; sc(T);
    while(T--){
        scanf("%s",s+1);
        int n=strlen(s+1); tot=n;
        A.init();
        ans=1e9;
        for(int i=1;i<=n;i++) {
            int x;
            if (s[i]==‘A‘) x=0;else
            if (s[i]==‘G‘) x=1;else
            if (s[i]==‘C‘) x=2;else
                            x=3;
            A.add(x);
        }
        printf("%d\n",ans);
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/zhangbuang/p/11580860.html

时间: 2024-10-27 13:12:14

G - Virus synthesis(回文树+dp)的相关文章

bzoj4044/luoguP4762 [Cerc2014]Virus synthesis(回文自动机+dp)

bzoj Luogu 你要用ATGC四个字母用两种操作拼出给定的串: 1.将其中一个字符放在已有串开头或者结尾. 2.将已有串复制,然后reverse,再接在已有串的头部或者尾部. 一开始已有串为空.求最少操作次数. len<=100000 题解时间 一个非空串经过一次操作2之后一定是一个偶回文串. 所以考虑建出PAM之后dp. 由于只有偶回文串可以通过操作2产生,所以只有偶回文串参与dp. 对于PAM上一个点 $ x $ : 如果这个串去掉两头一个字母之后的串 $ y $ 非空,那么很明显 $

[BZOJ4044]Virus synthesis 回文自动机的DP

4044: [Cerc2014] Virus synthesis Time Limit: 20 Sec  Memory Limit: 128 MB Description Viruses are usually bad for your health. How about fighting them with... other viruses? In this problem, you need to find out how to synthesize such good viruses. W

luogu P4762 [CERC2014]Virus synthesis (回文自动机)

大意: 初始有一个空串, 操作(1)在开头或末尾添加一个字符. 操作(2)在开头或末尾添加该串的逆串. 求得到串$S$所需最少操作数. 显然最后一定是由某个偶回文通过添加字符得到的, 那么只需要求出所有偶回文的最少操作数即可. 结论: 偶回文最后一次进行翻倍操作一定最优. 证明考虑数学归纳, 对于长为$2$的回文串显然成立. 对长度$>2$的偶回文串$A$, 记最后一次翻倍得到的串$B$, $B$的一半为$C$. 记$f(S)$为串$S$的最优值, 就有$f(B)=f(C)+1$. 考虑由$B$

【BZOJ2342】双倍回文(回文树)

[BZOJ2342]双倍回文(回文树) 题面 BZOJ 题解 构建出回文树之后 在\(fail\)树上进行\(dp\) 如果一个点代表的回文串长度为\(4\)的倍数 并且存在长度为它的一半的回文后缀 那么就是可行的 如何维护长度是一半的回文后缀? \(fail\)树上的父亲一定包括了它的所有的回文后缀 因此在\(fail\)树上\(dfs\),同时记录一下每个长度的回文出现的次数 这样访问到一个节点就可以直接检查了 #include<iostream> #include<cstdio&g

P4555 [国家集训队]最长双回文串(回文树)

题目描述 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为abc,逆序为cba,不相同). 输入长度为 n 的串 S ,求 S 的最长双回文子串 T ,即可将 T 分为两部分 X , Y ,( |X|,|Y|≥1 )且 X 和 Y都是回文串. 输入输出格式 输入格式: 一行由小写英文字母组成的字符串 S . 输出格式: 一行一个整数,表示最长双回文子串的长度. 输入输出样例 输入样例#1: 复制 baacaabbacabb 输出样例#1: 复制 12

Codeforces 932G Palindrome Partition - 回文树 - 动态规划

题目传送门 通往???的传送点 通往神秘地带的传送点 通往未知地带的传送点 题目大意 给定一个串$s$,要求将$s$划分为$t_{1}t_{2}\cdots t_{k}$,其中$2\mid k$,且$t_{i} = t_{k - i}$,问方案数. 直接做不太好做.虽然可以$O(n^{2})$进行动态规划. 考虑做一步转化:设$s' = s_{1}s_{n}s_{2}s_{n - 1}\cdots s_{n / 2}s_{n / 2 + 1}$. 然后它的一个偶回文划分可以和原来的划分一一对应.

回文树

(没有坑怎么填?) 最近膜了一些关于回文串的题目,感到非常有意思,遂开篇记录. 在逛UOJ的题目时发现了vfk添上了新题,APIO 2014的题目.本身是一件很正常的事,而它事实上也没有变成什么了不得的事.我看到了Palindrome这个标题---回文串已经烂大街了,没什么新意.不过我很早就向学习回文树这东西了,久仰其大名而未尝真正去了结果它,于是我就顺手撸了一把豪哥的论文,发现他讲解的实在是晦涩难懂---论文的通病,就是虽然表述没有歧义,但是难以理解.嘛,然后我就找了几个标程,发现回文树这东西

回文树或者回文自动机,及相关例题

回文树简述 在大部分说法中,回文树与回文自动机指的是一个东西: 回文树是对一个字符串,基于自动机思想构建的处理回文问题的树形结构: 回文树是对着一个单串建立的: 于是他主要用于计数(回文子串种类及个数) 基本建立思路是先建立其前缀的回文树,然后每加上一个字符,统计影响: 回文树存在fail指针但一般不承接字符串匹配问题: (回文树大概可以判定一个回文串是不是一个串的子串,但KMP之类的可以做得更好) 构建好的回文树,是这样的: (好难看) 可看出: 存在两个树结构,分别记录奇数|偶数长度的回文:

bzoj3676: [Apio2014]回文串 回文树

回文树的裸题. #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=500100; const int INF=1e9+10; struct PalinTree { int ch[maxn][26],f[maxn]; int