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