<学习笔记> manacher 和 回文自动机
manacher#
对于奇串,考虑维护一个具有最大
code
int manacher(int n){
for(int i=1,l=0,r=-1;i<=n;i++){
int k;
if(i>r) k=1;
else k=min(r-i+1,f[r+l-i]);
while( i-k>=1 && i+k<=n &&s[i-k]==s[i+k]) k++;
f[i]=k--;
ans=max(ans,f[i]*2-1);
if(r<i+k){
r=i+k;
l=i-k;
}
}
for(int i=1,l=0,r=-1;i<=n;i++){
int k;
if(i>r) k=0;
else k=min(r-i+1,g[l+r-i+1]);
while(i-k-1>=1 && i+k<=n && s[i-k-1]==s[i+k]) k++;
g[i]=k--;
ans=max(ans,g[i]*2);
if(r<i+k){
r=i+k;
l=i-k-1;
}
}
return ans;
}
回文自动机(回文树,PAM)#
对于一个新加入的字符
对于每个节点表示的回文串其实就是:从它到根再从根到它。
构建#
为了区分奇偶串,建两个根
每加入一个字符,在当前最长后缀回文串中找到最长的可以匹配新加字符的回文串,如果没有这个节点就新增一个节点,这样本质不同回文串个数加一,并更新
code
char s[N];
int len[N],fail[N],num[N],tr[N][26],tot=1,cur=0,pos=0;
int getfail(int x,int i){
while(i-len[x]-1<=0 || s[i-len[x]-1]!=s[i]) x=fail[x];
return x;
}
void solve(){
int n=strlen(s+1);
fail[0]=1;
len[1]=-1;
int last=0;
for(int i=1;i<=n;i++){
pos=getfail(cur,i);
if(!tr[pos][s[i]-'a']){
fail[++tot]=tr[getfail(fail[pos],i)][s[i]-'a'];// 注意是 getfail(fail[pos],i),
// 如果是 getfail(pos,i) 就会匹配自己
tr[pos][s[i]-'a']=tot;
len[tot]=len[pos]+2;
num[tot]=num[fail[tot]]+1;
}
cur=tr[pos][s[i]-'a'];
}
}
例题#
双倍回文#
PAM做法:其实可以贡献的回文串是它存在一个后缀回文串长度为它的
code
#include<bits/stdc++.h>
using namespace std;
const int N=5*1e5+10;
char s[N];
int fail[N],len[N],tr[N][26],tot=1,pos=0,cur=0;
int get_fail(int x,int i){
while(i-len[x]-1<=0 || s[i-len[x]-1]!=s[i]) x=fail[x];
return x;
}
int head[N*2],nex[N*2],ver[N*2],idx=0;
void add(int x,int y){
ver[++idx]=y,nex[idx]=head[x],head[x]=idx;
}
int cnt[N];
int ans;
void dfs(int x){
cnt[len[x]]++;
if(len[x]%4==0){
if(cnt[len[x]/2]) ans=max(ans,len[x]);
}
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
dfs(y);
}
cnt[len[x]]--;
}
signed main(){
int n;
scanf("%d",&n);
scanf("%s",s+1);
fail[0]=1,len[1]=-1;
add(1,0);
ans=0;
for(int i=1;i<=n;i++){
pos=get_fail(cur,i);
if(!tr[pos][s[i]-'a']){
fail[++tot]=tr[get_fail(fail[pos],i)][s[i]-'a'];
add(fail[tot],tot);
tr[pos][s[i]-'a']=tot;
len[tot]=len[pos]+2;
}
cur=tr[pos][s[i]-'a'];
}
dfs(1);
printf("%d",ans);
}
P4762 [CERC2014] Virus synthesis#
可以发现加的逆串越多越优。
然后发现一个串加逆串就构成一个回文串,而且贡献就是一个回文串加上剩余的。设
考虑有什么转移,假如树上有条边
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
char s[N];
int tr[N][5],fail[N],len[N],pos,cur,f[N],tot=1;
int trans[N],rk[N];
int getfail(int x,int i){
while(i-len[x]-1<=0 || s[i-len[x]-1]!=s[i]) x=fail[x];
return x;
}
signed main(){
int T;
scanf("%d",&T);
rk['A']=0,rk['T']=1,rk['C']=2,rk['G']=3;
while(T--){
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=0;i<=n+2;i++){
fail[i]=len[i]=trans[i]=0;
f[i]=(1<<25);
tr[i][0]=tr[i][1]=tr[i][2]=tr[i][3]=0;
}
tot=1,pos=cur=0;
fail[0]=1,len[1]=-1;
int ans=n;
f[0]=f[1]=1;
for(int i=1;i<=n;i++){
pos=getfail(cur,i);
if(!tr[pos][rk[s[i]]]){
fail[++tot]=tr[getfail(fail[pos],i)][rk[s[i]]];
tr[pos][rk[s[i]]]=tot;
len[tot]=len[pos]+2;
if(len[tot]<=2) trans[tot]=fail[tot];
else{
int tmp=trans[pos];
while(s[i-len[tmp]-1]!=s[i] || (len[tmp]+2)*2>len[tot]) tmp=fail[tmp];
trans[tot]=tr[tmp][rk[s[i]]];
}
}
if(len[tot]%2==0){
int tmp=trans[tot];
f[tot]=min(f[pos]+1,f[tot]);
f[tot]=min(f[tot],f[tmp]+len[tot]/2-len[tmp]+1);
ans=min(ans,f[tot]+n-len[tot]);
}
cur=tr[pos][rk[s[i]]];
}
for(int i=2;i<=n;i++) f[i]=len[i];
queue<int> q;
for(int i=0;i<4;i++){
if(tr[0][i]) q.push(tr[0][i]);
}
while(!q.empty()){
int x=q.front();
q.pop();
f[x]=min(f[x],len[x]/2-len[trans[x]]+f[trans[x]]+1);
for(int i=0;i<4;i++){
if(tr[x][i]){
q.push(tr[x][i]);
f[tr[x][i]]=min(f[x]+1,f[tr[x][i]]);
}
}
ans=min(ans,f[x]+n-len[x]);
}
printf("%d\n",ans);
}
}
作者:bloss
出处:https://www.cnblogs.com/jinjiaqioi/p/17929421.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效