/*---------------倍增算法+RMQ后缀数组模板--------------

输入:从0开始的字符串g,长度len最大为10^6
输出:
 sa[]表示:n 个后缀从小到大进行排序之后把排好序的后缀的开头位置顺
 次放入 sa 中,sa[i]表示排第i位的字符串开头是sa[i],因为添加了一个结尾0,所以sa[0]=len
 height 数组(h[]):定义 h[i]=suffix(sa[i-1])和 suffix(sa[i])的最长公
 共前缀,也就是排名相邻的两个后缀的最长公共前缀。
 mrank[i]表示:以i位开头的字符串,排名为mrank[i]。mrank[len]=0;
 cal(x,y):查询g[b]g[b+1]...g[len-1]与g[d]g[d+1]...g[len-1]的最大前缀数。
复杂度:空间复杂度33*len,时间复杂度,建立后缀数组为len*log(len),若不需要建立RMQ为len
 
--------------------------------------------------*/


int r[N];
int sa[N];
int scnt[N];
int wa[N],wb[N],wv[N];
int mrank[N];
int h[N],th[N];
int rmqdp[N][22];
int savek[N];
 
int cmp(int gg[],int a,int b,int k)
{
    return gg[a]==gg[b] && gg[a+k]==gg[b+k];
}
 
void getsa(int str[],int sa[],int n,int m)
{
    int i,*x,*y,*t;
    x=wa; y=wb;
    memset(scnt,0,sizeof(scnt));
    for(i=0;i<n;i++)
            scnt[ x[i]=str[i] ]++;
    for(i=1;i<m;i++)
            scnt[i]+=scnt[i-1];
    for(i=0;i<n;i++)
            sa[ --scnt[ str[i] ] ]=i;
 
    for(int p=1,j=1;p<n;j*=2,m=p)
    {
        for(p=0,i=n-j;i<n;i++) y[p++]=i;
        for(i=0;i<n;i++) if( sa[i]>=j ) y[p++]=sa[i]-j;
        for(i=0;i<n;i++) wv[i]=x[ y[i] ];
        memset(scnt,0,sizeof(scnt));
        for(i=0;i<n;i++) scnt[ wv[i] ]++;
        for(i=1;i<m;i++) scnt[i]+=scnt[i-1];
        for(i=n-1;i>=0;i--) sa[ --scnt[ wv[i] ] ] = y[i];
        for(p=1,t=x,x=y,y=t,x[sa[0]]=0,i=1;i<n;i++)
            x[ sa[i] ] = cmp(y,sa[i],sa[i-1],j)?p-1:p++;
    }
}
 
void geth(int str[],int n)
{
    h[n-1]=0;
    int p=0;
    for(int i=0;i<n-1;i++)
    {
        int tmp=mrank[i];
        while( str[i+p] == str[ sa[tmp-1]+p ] ) p++;
        h[i]=p;
        p--;
        p=max(0,p);
    }
}
 
void buildst(int n)
{
    for(int i=1;i<=n;i++)
        rmqdp[i][0] = th[i];
    for(int i=1;(1<<i)<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if( j+(1<<(i-1)) >n ) rmqdp[j][i]=rmqdp[j][i-1];
            else rmqdp[j][i]=min(rmqdp[j][i-1],rmqdp[j+(1<<(i-1))][i-1]);
        }
    }
}


int cal(int x,int y)
{
        x=mrank[x]; y=mrank[y];
        if(x>y) swap(x,y);
        x++;
        //然后就是求x到y的最小值
        int k = savek[y-x+1];
        return min(rmqdp[x][k],rmqdp[y-(1<<k)+1][k]);
}

void SuffixInit(char gg[],int len)
{
    //初始化RMQ中得k
    for(int i=1;i<N;i++)
        savek[i] = floor( log((double)i)/log(2.0) );
    
    for(int i=0;i<len;i++)
        r[i]=gg[i];
    
    r[len++]=0;
    getsa(r,sa,len,300);
    for(int i=0;i<len;i++)
        mrank[ sa[i] ]=i;
    //for(int i=0;i<len;i++) printf("%s\n",g+sa[i]);
    geth(r,len);
    for(int i=0;i<len-1;i++)
        th[ mrank[i] ]= h[i];
    buildst(len-1);
    
    /*
     int b,d;
     while(scanf("%d%d",&b,&d))
     {
        printf("%d\n",cal( b,d ));//查询g[b]g[b+1]...g[len-1]与g[d]g[d+1]...g[len-1]的最大前缀数。
     }
     */
}

 

//求sa更快的3DC3 
#define N 20020
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)

int wa[N],wb[N],wv[N],mws[N];
char s[N];
int str[3*N];
int sa[3*N];
int mrank[N];
int h[N];
int c0(int *r,int a,int b)
{
    return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
}

int c12(int k,int *r,int a,int b)
{
    if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
    else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];
}

void msort(int *r,int *a,int *b,int n,int m)
{
    int i;
    for(i=0;i<n;i++) wv[i]=r[a[i]];
    for(i=0;i<m;i++) mws[i]=0;
    for(i=0;i<n;i++) mws[wv[i]]++;
    for(i=1;i<m;i++) mws[i]+=mws[i-1];
    for(i=n-1;i>=0;i--) b[--mws[wv[i]]]=a[i];
    return;
}
void dc3(int *r,int *sa,int n,int m)
{
    int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
    r[n]=r[n+1]=0;
    for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
    msort(r+2,wa,wb,tbc,m);
    msort(r+1,wb,wa,tbc,m);
    msort(r,wa,wb,tbc,m);
    for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
    rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
    if(p<tbc) dc3(rn,san,tbc,p);
    else for(i=0;i<tbc;i++) san[rn[i]]=i;
    for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
    if(n%3==1) wb[ta++]=n-1;
    msort(r,wb,wa,ta,m);
    for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
    for(i=0,j=0,p=0;i<ta && j<tbc;p++)
    sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
    for(;i<ta;p++) sa[p]=wa[i++];
    for(;j<tbc;p++) sa[p]=wb[j++];
    return;
}
void geth(int str[],int n)
{
    for(int i=0;i<n;i++)
        mrank[ sa[i] ]=i;
    h[n-1]=0;
    int p=0;
    for(int i=0;i<n-1;i++)
    {
        int tmp=mrank[i];
        while( str[i+p] == str[ sa[tmp-1]+p ] ) p++;
        h[i]=p;
        p--;
        p=max(0,p);
    }
}
int main()
{
    while(scanf("%s",s))
    {
        int len=strlen(s);
        int n=0;
        for(int i=0;i<len;i++)
            str[n++]=s[i];
        str[n++]=0;
        dc3(str,sa,n,256);
        geth(str,n);
        //已经得到h
    }
    return 0;
}