#include<bits/stdc++.h> //CLOCKS_PER_SEC #define se second #define fi first #define ll long long #define Pii pair<int,int> #define Pli pair<ll,int> #define ull unsigned long long #define ALL(x) x.begin(),x.end() #define pb push_back //#define mp make_pair #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) const int maxn=3e5+10; const int inf=0x3f3f3f3f; const int mod=1e9+7; using namespace std; typedef double db; const db eps=1e-10; const db pi=acos(-1); const int MAXN=300005; const int N=26; int ans[maxn],p[maxn*2]; char s[maxn]; void Manacher(char *s,int n,int *pa){ pa[0] = 1; for(int i=1,j=0;i<(n<<1)-1;++i){ int p = i >> 1 , q = i - p , r = ((j + 1)>>1) + pa[j] - 1; pa[i] = r < q ? 0 : min(r - q + 1 , pa[(j<<1) - i]); while(0 <= p - pa[i] && q + pa[i] < n && s[p - pa[i]] == s[q + pa[i]]) pa[i]++; if(q + pa[i] - 1 > r) j = i; } } inline bool check(int L,int R) { int mid=L+R>>1; if ((L&1)==(R&1)) return p[mid<<1]>=R-L+2>>1; return p[mid<<1|1]>=R-L+1>>1; } inline bool Check(int L,int R) { int mid=L+R>>1; if (!check(L,mid) || !check(mid+((L&1)!=(R&1)),R)) return false; return true; } struct Palindromic_Tree { int nxt[MAXN][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成 int fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点 int cnt[MAXN]; int st[MAXN];//存放每个回文串的结尾坐标 int len[MAXN];//len[i]表示节点i表示的回文串的长度 int S[MAXN];//存放添加的字符 int last;//指向上一个字符所在的节点,方便下一次add int n;//字符数组指针 int p;//节点指针 int newnode(int l){//新建节点 for(int i=0;i<N;++i) nxt[p][i]=0; cnt[p]=0; st[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; } int get_fail(int x){//和KMP一样,失配后找一个尽量最长的 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(!nxt[cur][c]){//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串 int now=newnode(len[cur]+2) ;//新建节点 fail[now]=nxt[get_fail(fail[cur])][c] ;//和AC自动机一样建立fail指针,以便失配后跳转 nxt[cur][c]=now ; } last=nxt[cur][c] ; cnt[last]++; st[last]=n; } void count(){ for(int i=p-1;i>=0;--i) cnt[fail[i]]+=cnt[i]; //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串! } void get_ans(){ for(int i=0;i<p;i++){ if(len[i]>0){ if(Check(st[i]-len[i],st[i]-1)){ ans[len[i]]+=cnt[i]; } } } } }PAM; int main(){ while(~scanf("%s",&s)){ PAM.init(); int l=strlen(s); for(int i=1;i<=l;i++){ ans[i]=0; } Manacher(s,l,p); for(int i=0;s[i];i++){ PAM.add(s[i]); } PAM.count(); PAM.get_ans(); for(int i=1;i<=l;i++){ printf("%d",ans[i]); if(i!=l) printf(" "); else printf(" "); } } }