PAM

【JZOJ4752】字符串合成

by AmanoKumiko

Description

有一个空串\(S\)和目标串\(T\)

支持四种操作

1.将\(S\)变为\(P+S\)​,代价为\(|P|\)

2.将\(S\)变为\(S+P\),代价为\(|P|\)

3.将\(S\)翻转,接在前面,代价为\(1\)

3.将\(S\)翻转,接在后面,代价为\(1\)

Input

第一行数据组数\(T\)

下面\(T\)行每行一个串\(S\)

Output

\(T\)行,每行包含一个整数,表示最小代价

Sample Input

7
c
aaaab
bbaaaacc
ababa
abba
baab
aaabacdbbdcabaaaaaaaaaaaab

Sample Output

1
4
7
5
3
3
18

Data Constraint

对于100%的数据,1≤|S|≤100000,T≤10

Solution

显然,答案肯定是先变成一个回文串,然后两边加字符

有个很大胆的猜想,偶数长回文串最后一步一定是翻转(证明作练习

那我们就直接在PAM上dp

对于奇数长串:

因为奇数长不能翻转,所以

\[f[i]=min(f[fa[i]]+2,f[fail[i]]+len[i]-len[fail[i]]) \]

对于偶数长串:

由于上面的最后一步必定是翻转,所以

\[f[i]=min(f[fa[i]]+1,f[half[i]]+1+\frac{len[i]}{2}-len[half[i]]) \]

\(f[fa[i]]+1\)是因为可以在父亲节点翻转前加入一个字符

\(half[i]\)​表示最长的长度不超过\(\frac{len[i]}{2}\)\(i\)​​​的回文串后缀所在的节点

\(f[half[i]]+1+\frac{len[i]}{2}-len[half[i]]\)即补全后翻转

关于求\(half\)

由于它其实就是\(fail\)加了个长度的限制,那我们直接利用\(fail\)来求

建出一棵\(fail\)

用深搜+指针轻松解决(因为长度不降)

当然,懒一点的话可以直接暴力跳\(fail\),多个\(log\),不过跑不满

Code

#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define Fs(i,a) for(int i=last[a];i;i=e[i].next)
#define inf 2147483647
#define N 100010

int T,n,fa[N],tot,last[N],q[N],he,ans,pos;
char s[N];
struct node{int en,next;}e[N];
void add(int a,int b){e[++tot]=(node){b,last[a]};last[a]=tot;}
struct PAM{
	int cnt,lp,st[N],fa[N],half[N],fail[N],len[N],son[N][26],f[N];
	int getfail(int p,int x){while(s[p-len[x]-1]!=s[p])x=fail[x];return x;}
	void build(){
		memset(son,0,sizeof(son));
		len[1]=-1;
		lp=cnt=fail[0]=fail[1]=1;
		scanf("%s",s+1);
		n=strlen(s+1);
		F(i,1,n){
			int now=getfail(i,lp);
			if(!son[now][s[i]-'a']){
				cnt++;
				fail[cnt]=son[getfail(i,fail[now])][s[i]-'a'];
				son[now][s[i]-'a']=cnt;
				len[cnt]=len[now]+2;
				fa[cnt]=now;
			}
			lp=son[now][s[i]-'a'];
		}
		tot=0;
		memset(last,0,sizeof(last));
		F(i,0,cnt)if(i!=1)add(fail[i],i);
	}
	void dfs(int now,int pre){
		int op=pos;
		while(len[q[pos+1]]<=len[now]/2&&pos<he)pos++;
		half[now]=q[pos];
		Fs(i,now){
			int go=e[i].en;
			if(go==pre)continue;
			q[++he]=go;
			dfs(go,now);
			he--;
		}
		pos=op;
	}
	void calc(){
		ans=n;
		F(i,2,cnt){
			if(len[i]&1){
				f[i]=(fa[i]==1?1:min(f[fa[i]]+2,f[fail[i]]+len[i]-len[fail[i]]));
			}else{
				f[i]=(!fa[i]?2:f[fa[i]]+1);
				if(half[i])f[i]=min(f[i],f[half[i]]+len[i]/2-len[half[i]]+1);
			}
			ans=min(ans,f[i]+n-len[i]);
		}
	}
}t;

int main(){
	scanf("%d",&T);
	while(T--){
		t.build();
		pos=0;
		t.dfs(1,1);
		t.calc();
		printf("%d\n",ans);
	}
	return 0;
}