【JZOJ4752】字串合成
阿新 • • 發佈:2021-08-11
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; }