正题
题目链接:https://www.luogu.com.cn/problem/P5496
题目大意
长度为nnn的字符串,求每个字符串作为结尾有多少个回文串。
解题思路
PAMPAMPAM。
下面是个人对PAMPAMPAM的一些理解(不是讲解):
- 每个节点表示一个回文串,就是根到其的路径上的字符为一半。
- 为了方便表示长度为奇数的和偶数的,这里用了两个根节点。一个奇数根长度为−1-1−1,偶数根长度为000。偶数根的failfailfail指向奇数根。
- failfailfail指针表示该回文串的最长回文后缀(不包括整串)
- 加入新节点时,在上个节点的基础上不停的往后跳failfailfail直到该回文串的前一个字符等于当且字符。
- 新节点的failfailfail值同理在新点的父节点的failfailfail上面跳failfailfail。
- 时间复杂度O(n)O(n)O(n),空间复杂度O(nT)O(nT)O(nT)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e6+10;
int n,len[N],fail[N],num[N],next[N][26],cnt,m,last;
char s[N];
int get_fail(int x){for(;s[n-len[x]-1]!=s[n];)x=fail[x];return x;
}
int Insert(){int x=get_fail(last);if(!next[x][s[n]]){len[++cnt]=len[x]+2;int y=get_fail(fail[x]);fail[cnt]=next[y][s[n]];num[cnt]=num[fail[cnt]]+1;next[x][s[n]]=cnt;}return (last=next[x][s[n]]);
}
int main()
{scanf("%s",s+1);m=strlen(s+1);s[0]=26;len[1]=-1;fail[0]=cnt=1;int k=0;for(n=1;n<=m;n++){s[n]=(s[n]-'a'+k)%26;printf("%d ",k=num[Insert()]);}
}