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

bzoj Luogu

你要用ATGC四个字母用两种操作拼出给定的串:

1.将其中一个字符放在已有串开头或者结尾。

2.将已有串复制,然后reverse,再接在已有串的头部或者尾部。

一开始已有串为空。求最少操作次数。

len<=100000

题解时间

一个非空串经过一次操作2之后一定是一个偶回文串。

所以考虑建出PAM之后dp。

由于只有偶回文串可以通过操作2产生,所以只有偶回文串参与dp。

对于PAM上一个点 $ x $ :

  • 如果这个串去掉两头一个字母之后的串 $ y $ 非空,那么很明显 $ x $ 可以由 $ y $ 在操作2之前先加上这个字母获得, $ dp[x]=dp[y]+1 $ 。

  • 而如果 $ y $ 为空,那么需要至少两次操作, $ dp[x]=2 $ 。

  • $ x $ 可以由长度不超过 $ len/2 $ 的后缀回文串延长复制得来,倍增跳fail找到第一个长度不超过 $ len/2 $ 的串 $ y $ , $ dp[x]=dp[y]+(len[x]/2-len[y])+1 $ 。

#include<bits/stdc++.h>
using namespace std;
namespace RKK
{
const int N=100011;
char str[N];int n;
int cc(char ch)
{
switch(ch)
{
case 'A':return 0;
case 'G':return 1;
case 'C':return 2;
case 'T':return 3;
default:return 114514;
}
}
queue<int> q;
struct remilia{int tranc[4],len,fail;void set(){memset(this,0,24);}};
struct sakuya
{
remilia p[N];
int size,fin;
int fa[N][20];
int dp[N];
void set()
{
p[0].set(),p[1].set();
p[0].len=0,p[1].len=-1;
p[0].fail=p[1].fail=1;
memset(dp,0,(size+1)*sizeof(int));
size=fin=1;
}
sakuya(){size=fin=1;this->set();}
int match(char *s,int i,int px){return s[i-p[px].len-1]==s[i];}
void ins(char *s,int i)
{
int ch=cc(s[i]);
int npx,lpx,lpy;
lpx=fin;
while(!match(s,i,lpx)) lpx=p[lpx].fail;
if(!p[lpx].tranc[ch])
{
npx=++size;p[npx].set();
p[npx].len=p[lpx].len+2;
lpy=p[lpx].fail;
while(!match(s,i,lpy)) lpy=p[lpy].fail;
p[npx].fail=p[lpy].tranc[ch];
p[lpx].tranc[ch]=npx;
}
fin=p[lpx].tranc[ch];
}
int find(int x)
{
int l=p[x].len/2;
for(int k=19;k>=0;k--)if(p[fa[x][k]].len>l) x=fa[x][k];
while(p[x].len&1||p[x].len>l) x=fa[x][0];
return x;
}
void work()
{
for(int i=0;i<=size;i++) fa[i][0]=p[i].fail;
for(int k=1;k<20;k++)for(int i=0;i<=size;i++) fa[i][k]=fa[fa[i][k-1]][k-1];
dp[0]=0;for(int i=0;i<4;i++)if(p[0].tranc[i]) dp[p[0].tranc[i]]=2,q.push(p[0].tranc[i]);
int ans=n;
while(!q.empty())
{
int x=q.front();q.pop();
int f=find(x);
dp[x]=min(dp[x],dp[f]+p[x].len/2-p[f].len+1);
ans=min(ans,n-p[x].len+dp[x]);
for(int i=0;i<4;i++)if(p[x].tranc[i]) dp[p[x].tranc[i]]=dp[x]+1,q.push(p[x].tranc[i]);
}
printf("%d\n",ans);
}
}pam;
int TAT;
int Iris()
{
scanf("%d",&TAT);
while(TAT--)
{
scanf("%s",str+1),n=strlen(str+1);
for(int i=1;i<=n;i++) pam.ins(str,i);
pam.work();
pam.set();
}
return 0;
}
}
int main(){return RKK::Iris();}

最新文章

  1. Openjudge 1.13-21:最大质因子序列(每日两水)
  2. $.ajax请求返回数据中status为200,回调的却是error?
  3. Yii2中的环境配置
  4. 移动端 几个css3属性的练习
  5. Eclipse启动时卡死解决方法
  6. 在JavaWeb中使用Log4j步骤
  7. bzoj 4358 permu
  8. java小程序 示例 菲薄垃圾数列
  9. MySQL 密码修改
  10. HDU 4813 Hard Code 水题
  11. 用C语言实现统计一个文件夹中各种文件的比例
  12. Swift函数的定义建议
  13. js url参数的获取和设置以及删除
  14. TensorFlow-谷歌深度学习库 手把手教你如何使用谷歌深度学习云平台
  15. SVN分支与合并【超详细的图文教程】(转载)
  16. mysql增删改查练习
  17. json--处理框架
  18. Jmeter(三十二)Jmeter Question 之 “自定义函数开发”
  19. day 20 类与类之间的关系,继承2
  20. LeetCode--110--平衡二叉树

热门文章

  1. Python基础—文件操作(Day8)
  2. elk监听Java日志发送微信报警
  3. JDK、JRE 和 JVM 有什么用,它们是怎样运行的
  4. selenium+python 处理只读日期控件的2种方法
  5. NSSCTF-[UTCTF 2020]Zero
  6. [杂记]LeTeX中使用tikz画图的一些实例
  7. 信而泰IPv6协议一致性测试解决方案
  8. 如何利用Smartbi做数据分析:车企销量排名TOP10的车型
  9. Go切片全解析
  10. Typora笔记上传到博客