题目链接
题意
给定一个长度为$n$的字符串,对于$1<=i<=n$,求满足长度为$i$,本身是回文串并且它的前一半也是回文串的子串个数。
题解
- 对于求长度为$x$的回文子串个数的问题可以用回文自动机在线性时间复杂度内解决。
- 但此题要求回文子串的前一半也是回文串,显然前一半是回文串等价于后一半也是回文串,那么我们可以在构建回文自动机的同时维护一个$half$数组,表示不超过当前回文串的长度的一半的最长回文后缀
- 构建half数组与fail数组类似,从当前节点fail指针指向节点的half数组转移过来即可
查看代码
#include <stdio.h>
#include <string.h>
using namespace std;
#define _for(i, a, b) for (int i = (a); i <= (b); ++i)
typedef long long ll;
const int maxn = 3e5 + 5;
const int mod = 1e9 + 7;
struct PAM
{
int len[maxn], fail[maxn], ch[maxn][26], cnt[maxn], tot, last, p, q;
int half[maxn], ans[maxn], n;
char s[maxn];
void init()
{
s[0] = -1, fail[0] = 1, fail[1] = 0;
len[0] = 0, len[1] = -1, tot = 1;
last = 0;
for (int i = 0; i <= n + 3; ++i)
{
cnt[i] = ans[i] = half[i] = 0;
for (int j = 0; j < 26; ++j)
ch[i][j] = 0;
}
}
int newnode(int x)
{
len[++tot] = x;
return tot;
}
int getfail(int x, int n)
{
while (s[n - len[x] - 1] != s[n])
x = fail[x];
return x;
}
void build()
{
for (int i = 1; s[i]; ++i)
{
s[i] -= 'a';
p = getfail(last, i);
if (!ch[p][s[i]])
{
q = newnode(len[p] + 2);
fail[q] = ch[getfail(fail[p], i)][s[i]];
ch[p][s[i]] = q;
if (len[q] <= 2)
half[q] = fail[q];
else
{
int tmp = half[p];
while (s[i - len[tmp] - 1] != s[i] || (len[tmp] + 2) * 2 > len[q] + 1)
tmp = fail[tmp];
half[q] = ch[tmp][s[i]];
}
}
++cnt[last = ch[p][s[i]]];
}
}
void cal()
{
for (int i = tot; i >= 0; --i)
{
cnt[fail[i]] += cnt[i];
if (len[half[i]] == (len[i] + 1) / 2 || len[i] == 1)
ans[len[i]] += cnt[i];
}
for (int i = 1; i < n; ++i)
{
printf("%d ", ans[i]);
}
printf("%d\n",ans[n]);
}
} pam;
int main()
{
#ifndef ONLINE_JUDGE
freopen("simple.in", "r", stdin);
freopen("simple.out", "w", stdout);
#endif
while (~scanf("%s", pam.s + 1))
{
pam.n = strlen(pam.s + 1);
pam.init();
pam.build();
pam.cal();
}
return 0;
}