回文自动机模板题:http://acm.hdu.edu.cn/showproblem.php?pid=6599
#include <bits/stdc++.h>
#define pb push_back
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAX = 3e5+100;
const int ALP = 26;
struct Palindromic_Tree {
int son[MAX][ALP]; //转移边
int fail[MAX]; //fail 指针
int cnt[MAX]; //当前节点表示的回文串在原串中出现了多少次
int num[MAX]; //当前节点 fail 可以向前跳多少次
int len[MAX]; //当前节点表示的回文串的长度
vector<int> to[MAX];
int S[MAX]; //插入的字符串
int last; //最后一次访问到的节点,类似 SAM
int n; //插入的字符串长度
int p; //自动机的总状态数
int newnode(int l) {
memset(son[p], 0, sizeof(son[p]));
cnt[p] = 0;
num[p] = 0;
len[p] = l;
return p++;
}
void init() {
p = 0;
newnode(0);
newnode(-1);
last = 0;
n = 0;
S[n] = -1;
fail[0] = 1;
to[1].pb(0);
}
int get_fail(int x) {
while (S[n - len[x] - 1] != S[n]) x = fail[x];
return x;
}
void add(int c) {
c -= 'a';
S[++n] = c;
int cur = get_fail(last); //通过上一次访问的位置去扩展
if (!son[cur][c]) { //如果没有对应的节点添加一个新节点
int now = newnode(len[cur] + 2);
fail[now] = son[get_fail(fail[cur])][c]; //通过当前节点的 fail 去扩展出新的 fail
to[son[get_fail(fail[cur])][c]].pb(now);
son[cur][c] = now;
num[now] = num[fail[now]] + 1; //记录 fail 跳多少次
}
last = son[cur][c];
cnt[last]++; //表示当前节点访问了一次
}
void count() {
//如果某个节点出现一次,那么他的 fail 也一定会出现一次,并且在插入的时候没有计数
for (int i = p - 1; i >= 0; i--) cnt[fail[i]] += cnt[i];
}
} AUT;
bool vis[MAX],flag[MAX];
int ans[MAX];
void dfs(int u) {
if(flag[(AUT.len[u]+1)/2]) ans[AUT.len[u]] += AUT.cnt[u];
if(AUT.len[u]>=0) flag[AUT.len[u]]=1;
for(auto v:AUT.to[u]) {
dfs(v);
}
if(AUT.len[u]>=0) flag[AUT.len[u]]=0;
}
int main() {
//freopen("a.txt","r",stdin);
ios::sync_with_stdio(0);
string s;
while(cin>>s) {
int siz = s.length();
rep(i, 0, siz) {
AUT.to[i].clear();
ans[i] = 0;
}
AUT.init();
//rep(i, 0, siz) AUT.to[i].clear();
for(auto x:s) AUT.add(x);
AUT.count();
dfs(1);
ans[1] = siz;
rep(i, 1, siz) {
cout<<ans[i];
if(i!=siz) cout<<' ';
else cout<<endl;
}
}
return 0;
}