P4762 [CERC2014]Virus synthesis
最后一定是一个大回文串+一些暴力边角。
多维护一个转移 \(tra[p]\),表示 \(\leq \frac{len[p]}{2}\) 的后缀位于的状态。
显然有两种转移:
- 直接填一个字符,\(f[v]=f[u]+1\)
- 由 \(tra[p]\) 反转+暴力添加字符得来。
利用拓扑排序DP即可。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
register char s; while(!isdigit(s=getchar())) f=s=='-'?-1:f;
do x=x*10+(s^48); while(isdigit(s=getchar())); return x*f;
} const int N=100010;
int T,n,tot,lst;
int fa[N],c[N][4],len[N],f[N],tra[N];
char s[N];
inline int cvt(const char& ch) {
if(ch=='A') return 0;
if(ch=='C') return 1;
if(ch=='G') return 2;
return 3;
}
inline int jmp(int p,int i)
{while(s[i-len[p]-1]!=s[i]) p=fa[p]; return p;}
inline void ext(int ch,int i) {
R p=jmp(lst,i); if(!c[p][ch]) {
R np=++tot; f[np]=len[np]=len[p]+2;
R t=jmp(fa[p],i);
fa[np]=c[t][ch],c[p][ch]=np;
if(len[np]<=2) tra[np]=fa[np];
else {
t=tra[p];
while(s[i-len[t]-1]!=s[i]||(len[t]+2)*2>len[np]) t=fa[t];
tra[np]=c[t][ch] ;
}
} lst=c[p][ch];
}
queue<int> q;
int ans;
inline void main() {
T=g(); s[0]='#';
while(T--) {
scanf("%s",s+1),n=strlen(s+1);
memset(c,0,(tot+1)*16);
len[fa[fa[1]=lst=0]=tot=1]=-1;
for(R i=1;i<=n;++i) ext(cvt(s[i]),i);
ans=n;
for(R i=0;i<4;++i) if(c[0][i]) q.push(c[0][i]);
while(q.size()) {
R u=q.front(); q.pop();
f[u]=min(f[u],f[tra[u]]+1+len[u]/2-len[tra[u]]);
ans=min(ans,n-len[u]+f[u]);
for(R i=0;i<4;++i) if(c[u][i]) {
R v=c[u][i];
f[v]=min(f[v],f[u]+1);
q.push(c[u][i]);
}
}
printf("%d\n",ans);
}
}
} signed main() {Luitaryi::main(); return 0;}
2020.01.16