zoukankan      html  css  js  c++  java
  • Codeforces Gym100543G Virus synthesis 字符串 回文自动机 动态规划

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF-100543G.html

    题目传送门 - CF-Gym100543G

    题意

      你可以对一个字符串进行以下两种操作:

      1.  在其头或者尾部加入一个新字符

      2.  翻转当前字符串,并把他拼接在当前字符串的前面或者后面

      给你 T 组询问,每组询问一个字符串,问你至少要多少次操作才能生成这个串。

      字符集 = ${'A','C','G','T'}$ ,字符串串长 $leq 100000$

    题解

      第一次写回文自动机。现学现用。

      写完调不出样例。网上看了看 Claris 的代码。研究了一下,继续调。样例是过了,一交 wa 。思索之后,重新打开 Claris 的博客。然后把代码改的和他差不多了 QAQ

      做法:

      我们先建一棵 PAM 。

      然后考虑在 PAM 上面 DP 。

      令当前串在 PAM 上面的状态为 $x$ 。

      考虑长度为 偶数 的回文串,分两种情况:

        折半,令节点 $y$ 为长度小于等于 $len_x$ 的一般的最长回文子串,则 $ dp_x=min(dp_x,dp_y+(cfrac {len_x}{2} - len_y) + 1) $

        删除两侧字符,令节点 $y$ 为当前回文串删除两侧节点得到,那么由于我们可以先折半再删除,所以 $dp_x=min(dp_x,dp_y+1)$ 。

      考虑长度为 奇数 的回文串,令 $dp_i=len_i$,分两种情况说明他是对的:

        该串下一步暴力填充至完成全串: 则选择该串不如直接选择偶串,故令 $dp_i=len_i$ 不亏。

        该串由偶串折半而来,那么由于在折半前有偶串的“删除两侧字符”这个转移,故不需要在奇串的转移中加入删除左侧或者右侧字符的转移 。

      具体做法见代码。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=100005;
    struct PAM{
    	static const int C=4;
    	int Next[N][C],fail[N],len[N],s[N],last,n,p;
    	int Half[N];
    	int newnode(int L){
    		memset(Next[p],0,sizeof Next[p]);
    		len[p]=L;
    		return p++;
    	}
    	void init(){
    		p=last=n=Half[0]=Half[1]=0;
    		newnode(0),newnode(-1);
    		s[0]=-1,fail[0]=1,fail[1]=0;
    	}
    	int getfail(int x){
    		while (s[n-len[x]-1]!=s[n])
    			x=fail[x];
    		return x;
    	}
    	void add(int c){
    		s[++n]=c;
    		int x=getfail(last);
    		if (!Next[x][c]){
    			int y=newnode(len[x]+2);
    			fail[y]=Next[getfail(fail[x])][c];
    			if (len[y]<=2)
    				Half[y]=fail[y];
    			else {
    				int z=Half[x];
    				while (s[n-len[z]-1]!=s[n]||(len[z]+2)*2>len[y])
    					z=fail[z];
    				Half[y]=Next[z][c];
    			}
    			Next[x][c]=y;
    		}
    		last=Next[x][c];
    	}
    }pam;
    int T,n,Turn[300],dp[N];
    int q[N],head,tail;
    char s[N];
    int main(){
    	Turn['A']=0,Turn['C']=1,Turn['G']=2,Turn['T']=3;
    	scanf("%d",&T);
    	while (T--){
    		scanf("%s",s);
    		n=strlen(s);
    		pam.init();
    		for (int i=0;i<n;i++)
    			pam.add(Turn[s[i]]);
    		// 考虑长度为 偶数 的串,分两种情况:
    		// 折半
    		// 删除两侧字符  
    		// 考虑长度为 奇数 的串,分两种情况:
    		// 该串下一步暴力填充至完成全串: 则选择该串不如直接选择偶串,故令 dp[i]=len[i]
    		// 该串由偶串折半而来,那么由于在折半前有偶串的“删除两侧字符”这个转移,故不需要在奇串的转移中加入删除左侧或者右侧字符的转移 
    		for (int i=2;i<pam.p;i++)
    			if (pam.len[i]&1)
    				dp[i]=pam.len[i];
    		int ans=n;
    		head=tail=0,dp[0]=1,q[++tail]=0;
    		while (head<tail)
    			for (int x=q[++head],i=0;i<4;i++){
    				int y=pam.Next[x][i];
    				if (!y)
    					continue;
    				dp[y]=min(dp[x]+1,pam.len[y]/2-pam.len[pam.Half[y]]+dp[pam.Half[y]]+1);
    				ans=min(ans,n-pam.len[y]+dp[y]);
    				q[++tail]=y;
    			}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    多线程:多线程设计模式(一):总体介绍
    javascript:12种JavaScript MVC框架之比较
    mysql 查询死锁语句
    charles 抓包工具破解方法
    java 自定义log类
    git统计日期之间的代码改动行数
    mac/linux自带定时任务执行crontab的使用
    python MD5步骤
    python 操作excel读写
    python logger日志工具类
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF-100543G.html
Copyright © 2011-2022 走看看