1. 程式人生 > 其它 >【JZOJ4752】字串合成

【JZOJ4752】字串合成

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;
}