#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int MAXN=1000005;
char str[MAXN];
int SA[MAXN]; //排名为i的后缀的起始位置
int RANK[MAXN]; //第i个后缀的排名
int tax[MAXN]; //计数排序辅助数组
int tp[MAXN]; //rank的辅助数组,第二关键字,意义同SA
int a[MAXN]; //原串
int height[MAXN];//LCP
int N,M; //M为当前排名的种类
int get_int(char c)
{
if(c>='A'&&c<='Z') return c-'A';
return c-'a'+26;
}
void init()
{
scanf("%s",str);
N=strlen(str);
for(int i=0;i<N;i++) a[i+1]=str[i];
}
void Rsort()
{
for(int i=0; i<=M; i++) tax[i]=0;
for(int i=1; i<=N; i++) tax[RANK[tp[i]]]++;
for(int i=1; i<=M; i++) tax[i]+=tax[i-1]; //排最低位(在i前面有多少个)
for(int i=N; i>=1; i--) SA[tax[RANK[tp[i]]]--]=tp[i];
}//第二关键字排在i位,其上一轮比较时的排名为rank在这个排名及之后有tax个,那么排名SA为tax的编号就是第二关键字排在tp的,因为第一关键字已经排好了
int cmp(int *f,int x,int y,int w) {return f[x]== f[y]&& f[x+w]== f[y+w];} //倍增
void Suffix()
{
for(int i=1; i<=N; i++) RANK[i]=a[i], tp[i]=i;
M=127, Rsort();
for(int w=1, p=1, i; p<N; w+=w, M=p) //w长度,M种不同排名
{
for(p=0, i=N-w+1; i<=N; i++) tp[++p]=i; //长度过长,超过部分设为0
for(i=1; i<=N; i++) if(SA[i]>w) tp[++p]=SA[i]-w; // 第二关键字排p位的
Rsort(); swap(RANK,tp); RANK[SA[1]]=p=1;//得到RANK,并把它赋给tp,用于后边
for(i=2;i<=N;i++) RANK[SA[i]]= cmp(tp,SA[i],SA[i-1],w)? p: ++p;//统计有多少种排名
}
//LCP
int j,k=0;
for(int i=1; i<=N; height[RANK[i++]]=k)//相关证明见训练指南
for(k=k? k-1: k, j=SA[RANK[i]-1]; a[i+k]== a[j+k]; k++);
}
void out()
{
for(int i=1;i<=N;i++) printf("%d ",SA[i]);
}
int main()
{
init(); Suffix(); out();
return 0;
}