题目链接: https://codeforces.com/contest/835/problem/D
注: 欢迎移步 https://codeforces.com/blog/entry/67839
题意: 一个回文串是\(1\)-回文的,如果一个回文串的左半部分和右半部分一样且都是\(k\)-回文串(右半部分是指长度为该串长度除以二下取整的后缀),则该串为\((k+1)\)回文串,满足该串是\(k\)回文串的最大\(k\)称作该串的回文级别。给定一个串\(s\), 对于每一个\(k=1,2,...,n\)求出该串中有多少个位置不同的\(k\)-回文串。\(n\le 5\times 10^6\). (除数据范围外与原题均相同)
题解: \(n^2\)的做法肯定是以子串为状态进行dp, 但是显然废状态太多了,只有回文串dp值非零,而一个串本质不同的回文串的个数是\(O(n)\)的。
所以,以本质不同的回文串作为状态进行dp. 本质不同的回文串一一对应回文自动机上的节点。设\(f[x]\)表示\(x\)节点代表回文串的回文级别。
然后转移方程显然: 若\(x\)不存在长度为\([\frac{x}{2}]\)(中括号表示下取整)的回文后缀,则\(dp[x]=1\), 否则\(dp[x]\)等于那个回文后缀的\(dp\)值+1.
dp完之后,我们求的是本质不同,但是题目要求求位置不同,所以还需要再统计一下每个点的子树大小。
时间复杂度\(O(n)\).
代码 (Codeforces 835D AC)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define llong long long
using namespace std;
const int N = 1e6+2;
const int LGN = 23;
const int S = 26;
int son[N+3][S+1];
int fail[N+3];
int len[N+3];
int sz[N+3];
int tsz[N+3];
char a[N+3];
int dp[N+3];
int bd[N+3];
int ord[N+3];
int buc[N+3];
llong ans[N+3];
int n,siz,lstpos;
void initPAM()
{
siz = lstpos = 1; fail[0] = fail[1] = 1; len[1] = -1; len[0] = 0; bd[0] = 0; bd[1] = 1;
}
void insertchar(int id)
{
int p = lstpos;
while(a[id]!=a[id-len[p]-1]) {p = fail[p];}
if(!son[p][a[id]])
{
siz++; int u = siz,v = fail[p];
while(a[id]!=a[id-len[v]-1]) {v = fail[v];}
fail[u] = son[v][a[id]]; son[p][a[id]] = u; len[u] = len[p]+2;
if(len[u]<=2) {bd[u] = fail[u];}
else
{
bd[u] = bd[p];
while(a[id]!=a[id-len[bd[u]]-1] || (len[bd[u]]+2)*2>len[u])
{
bd[u] = fail[bd[u]];
}
bd[u] = son[bd[u]][a[id]];
}
if(len[bd[u]]==(len[u]>>1)) {dp[u] = max(0,dp[bd[u]])+1;}
else {dp[u] = 1;}
}
sz[son[p][a[id]]]++;
lstpos = son[p][a[id]];
}
int main()
{
initPAM();
scanf("%s",a+1); n = strlen(a+1);
for(int i=1; i<=n; i++) a[i]-=96;
for(int i=1; i<=n; i++) {insertchar(i);}
for(int i=2; i<=siz; i++) {buc[len[i]]++;}
for(int i=1; i<=n; i++) {buc[i] += buc[i-1];}
for(int i=siz; i>=2; i--) {ord[(buc[len[i]]--)+1] = i;}
ord[0] = 1; ord[1] = 0;
for(int j=siz; j>=2; j--)
{
int u = ord[j];
sz[fail[u]] += sz[u];
}
for(int j=2; j<=siz; j++)
{
ans[dp[j]] += (llong)sz[j];
}
for(int j=LGN-1; j>=1; j--) ans[j] += ans[j+1];
for(int i=1; i<=n; i++) printf("%I64d\n",ans[i]);
return 0;
}