[CERC2014]Virus synthesis,洛谷P4762,中等的PAM

本文深入探讨了PAM算法在处理字符串匹配和分析中的应用,特别是如何利用PAM管理不同字符串,计算达到特定状态所需的步骤。文章详细解释了算法的实现过程,包括如何处理偶数长度的字符串以及如何通过后缀和转移边贡献答案。

正题

      中等难度,大概想了10分钟就会做了

      因为PAM可以管理所有本质不同的串,所以对于我们只需要考虑要多少步可以达到当前点的对应串,最后拿所有节点更新一下ans即可,具体更新就是n-len[x]+ans[x],ans[x]就是要多少步可以达到当前点的对应串.

      因为翻转一下只能变成偶串,所以我们先考虑偶串:

      对于一个偶串now的一个不超过长度/2后缀的后缀p,它对该串的贡献显然是len[now]/2-len[p]+ans[p]+1,考虑先补齐一半再翻转一下.相当于取ans[p]-len[p]+1中最小的,当长度增加时,len的减少速率肯定不比ans的增加速率慢,因为长度+1步数至多+1,所以我们只需要取最长的那个串.维护方法跟这题相同,不再赘述.

      但是ans也不一定是由这样的后缀来贡献的,还可能是通过转移边来贡献的,(至于为什么可以考虑一个子串要翻转变为当前偶数串,可以在前面加东西,也就是通过后缀来转移,也可以在后面加东西,也就是通过转移边来转移).因为偶根没有计算翻转的贡献,所以偶根的贡献为2,否则贡献就是ans[p]+1.

     

#include<bits/stdc++.h>
using namespace std;

const int N=10000010;

struct Palindrome_AutoMaton{
	int tot,n,T,las;
	int ch[N][26],fail[N],num[N],len[N],ans[N];
	char s[N];
	void g0(int x){ch[x][0]=ch[x][19]=ch[x][2]=ch[x][6]=0;}
	void init(){
		tot=1;las=0;fail[0]=1;fail[1]=0;len[1]=-1;
		g0(0);g0(1);
	}
	int gfail(int x,int pos){
		while(s[pos-len[x]-1]!=s[pos]) x=fail[x];
		return x;
	}
	void insert(int pos,int c){
		int p=gfail(las,pos);
		if(!ch[p][c]){
			int now=++tot;g0(now);
			fail[now]=ch[gfail(fail[p],pos)][c];
			len[now]=len[p]+2;
			int tmp=num[p];
			while(s[pos-len[tmp]-1]!=s[pos] || len[ch[tmp][c]]*2>len[now]) tmp=fail[tmp];
			num[now]=ch[tmp][c];ch[p][c]=now;
			if(len[now]%2==0) ans[now]=min(len[now]/2-len[num[now]]+ans[num[now]]+1,p==0?2:ans[p]+1);
			else ans[now]=len[now]-len[fail[now]]+ans[fail[now]];
		}
		las=ch[p][c];
	}
	void build(){
		scanf("%d",&T);
		while(T--){
			scanf("%s",s+1);init();n=strlen(s+1);
			for(int i=1;i<=n;i++) insert(i,s[i]-'A');
			int mmin=1e9;
			for(int i=2;i<=tot;i++) mmin=min(mmin,n-len[i]+ans[i]);
			printf("%d\n",mmin);
		}
	}
}PAM;

int main(){
	PAM.build();
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值