#include<bits/stdc++.h>
#define N 400040
#define lson tr[now].l
#define rson tr[now].r
using namespace std;

int n;
char s[200020];

struct SM
{
    struct point
    {
        int son[26],len,mx,at,fa;
    }t[N];

    struct tree
    {
        int sum,l,r;
    }tr[N<<5];

    int last=1,cnt=1,gg=0;
    int tot=0;
    int rt[N];
    int top[N],dp[N];
    bool sz[N],vis[N];
    vector<int> g[N];

    void add(int c)
    {
        int p=last;
        int np=++cnt;
        t[np].len=t[p].len+1;
        t[np].at=++gg;
        sz[np]=1;
        while(p&&(!t[p].son[c]))
        {
            t[p].son[c]=np;
            p=t[p].fa;
        }
        if(!p) t[np].fa=1;
        else
        {
            int q=t[p].son[c],nq;
            if(t[q].len==t[p].len+1)
            {
                t[np].fa=q;
            }
            else
            {
                nq=++cnt;
                t[nq]=t[q];
                t[nq].len=t[p].len+1;
                t[np].fa=t[q].fa=nq;
                t[nq].at=t[np].at;
                while(p&&(t[p].son[c]==q))
                {
                    t[p].son[c]=nq;
                    p=t[p].fa;
                }
            }
        }
        last=np;
    }

    void push_up(int now)
    {
        tr[now].sum=tr[lson].sum+tr[rson].sum;
    }

    void insert(int &now,int l,int r,int pos,int val)
    {
        if(!now) now=++tot;
        if(l==r)
        {
            tr[now].sum=val;
            return ;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)
        {
            insert(lson,l,mid,pos,val);
        }
        else
        {
            insert(rson,mid+1,r,pos,val);
        }
        push_up(now);
    }

    int query(int now,int l,int r,int ll,int rr)
    {
        if(ll>rr) return 0;
        if(!now) return 0;
        if(ll<=l&&r<=rr)
        {
            return tr[now].sum;
        }
        int mid=(l+r)>>1;
        if(rr<=mid)
        {
            return query(lson,l,mid,ll,rr);
        }
        else
        {
            if(ll>mid)
            {
                return query(rson,mid+1,r,ll,rr);
            }
            else
            {
                return query(lson,l,mid,ll,mid)+query(rson,mid+1,r,mid+1,rr);
            }
        }
    }

    int merge(int a,int b,int l,int r)
    {
        //printf("%d %d\n",a,b);
        if(!a) return b;
        if(!b) return a;
        int now=++tot;
        if(l==r)
        {
            tr[now].sum=1;
            return now;
        }
        int mid=(l+r)>>1;
        tr[now].l=merge(tr[a].l,tr[b].l,l,mid);
        tr[now].r=merge(tr[a].r,tr[b].r,mid+1,r);
        push_up(now);
        return now;
    }

    int dfs(int now)
    {
        if(sz[now])
        {
            insert(rt[now],1,n,t[now].at,1);
        }
        for(int i=0;i<g[now].size();i++)
        {
            dfs(g[now][i]);
            rt[now]=merge(rt[now],rt[g[now][i]],1,n);
        }
    }

    int dfs1(int now)
    {
        // printf("%d %d %d %d %d\n",now,t[now].fa,t[now].at-t[now].len+t[top[t[now].fa]].len,t[now].at-1,query(rt[top[t[now].fa]],1,n,t[now].at-t[now].len+t[top[t[now].fa]].len,t[now].at-1));
        // printf("%d %d %d %d\n",t[now].at,t[now].len,t[t[now].fa].at,t[t[]])
        if(query(rt[top[t[now].fa]],1,n,t[now].at-t[now].len+t[top[t[now].fa]].len,t[now].at-1))
        {
            dp[now]=dp[t[now].fa]+1;
            top[now]=now;
        }
        else
        {
            dp[now]=dp[t[now].fa];
            top[now]=top[t[now].fa];
        }
        // printf("%d %d\n",now,dp[now]);
        for(int i=0;i<g[now].size();i++)
        {
            dfs1(g[now][i]);
        }
    }

    int print(int now,int l,int r)
    {
        if(l==r)
        {
            if(tr[now].sum) printf("%d ",l);
            return 0;
        }
        int mid=(l+r)>>1;
        if(tr[lson].sum) print(lson,l,mid);
        if(tr[rson].sum) print(rson,mid+1,r);
    }

    int solve()
    {
        for(int i=1;i<=cnt;i++)
        {
            g[t[i].fa].push_back(i);
        }
        dfs(1);
        for(int i=0;i<g[1].size();i++)
        {
            top[g[1][i]]=g[1][i];
            dp[g[1][i]]=1;
            for(int j=0;j<g[g[1][i]].size();j++)
            {
                dfs1(g[g[1][i]][j]);
            }
        }
        int ans=0;
        for(int i=1;i<=cnt;i++)
        {
            ans=max(ans,dp[i]);
        }
        printf("%d\n",ans);
    }
}sm;

int main()
{
    scanf("%d",&n);
    scanf("%s",s);
    n=strlen(s);
    for(int i=0;i<n;i++)
    {
        sm.add(s[i]-'a');
    }
    sm.solve();
    // printf("%d\n",sm.tot);
    // for(int i=1;i<sm.cnt;i++)
    // {
    //     printf("%d %d\n",i,sm.t[i].at);
    // }
}


#include<bits/stdc++.h>
#define N 400040
#define lson tr[now].l
#define rson tr[now].r
using namespace std;

int n;
char s[200020];

struct SM
{
    struct point
    {
        int son[26],len,mx,at,fa;
    }t[N];

    struct tree
    {
        int sum,l,r;
    }tr[N<<4];

    int last=1,cnt=1,gg=0;
    int tot=0;
    int rt[N];
    int top[N],dp[N];
    bool sz[N],vis[N];
    vector<int> g[N];

    void add(int c)
    {
        int p=last;
        int np=++cnt;
        t[np].len=t[p].len+1;
        t[np].at=++gg;
        sz[np]=1;
        while(p&&(!t[p].son[c]))
        {
            t[p].son[c]=np;
            p=t[p].fa;
        }
        if(!p) t[np].fa=1;
        else
        {
            int q=t[p].son[c],nq;
            if(t[q].len==t[p].len+1)
            {
                t[np].fa=q;
            }
            else
            {
                nq=++cnt;
                t[nq]=t[q];
                t[nq].len=t[p].len+1;
                t[np].fa=t[q].fa=nq;
                t[nq].at=t[np].at;
                while(p&&(t[p].son[c]==q))
                {
                    t[p].son[c]=nq;
                    p=t[p].fa;
                }
            }
        }
        last=np;
    }

    void push_up(int now)
    {
        tr[now].sum=tr[lson].sum+tr[rson].sum;
    }

    void insert(int &now,int l,int r,int pos,int val)
    {
        if(!now) now=++tot;
        if(l==r)
        {
            tr[now].sum=val;
            return ;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)
        {
            insert(lson,l,mid,pos,val);
        }
        else
        {
            insert(rson,mid+1,r,pos,val);
        }
        push_up(now);
    }

    int query(int now,int l,int r,int ll,int rr)
    {
        if(ll>rr) return 0;
        if(!now) return 0;
        if(ll<=l&&r<=rr)
        {
            return tr[now].sum;
        }
        int mid=(l+r)>>1;
        if(rr<=mid)
        {
            return query(lson,l,mid,ll,rr);
        }
        else
        {
            if(ll>mid)
            {
                return query(rson,mid+1,r,ll,rr);
            }
            else
            {
                return query(lson,l,mid,ll,mid)+query(rson,mid+1,r,mid+1,rr);
            }
        }
    }

    int merge(int a,int b,int l,int r)
    {
        //printf("%d %d\n",a,b);
        if(!a) return b;
        if(!b) return a;
        int now=++tot;
        if(l==r)
        {
            tr[now].sum=1;
            return now;
        }
        int mid=(l+r)>>1;
        tr[now].l=merge(tr[a].l,tr[b].l,l,mid);
        tr[now].r=merge(tr[a].r,tr[b].r,mid+1,r);
        push_up(now);
        return now;
    }

    int dfs(int now)
    {
        if(sz[now])
        {
            insert(rt[now],1,n,t[now].at,1);
        }
        for(int i=0;i<g[now].size();i++)
        {
            dfs(g[now][i]);
            rt[now]=merge(rt[now],rt[g[now][i]],1,n);
        }
    }

    int dfs1(int now)
    {
        // printf("%d %d %d %d %d\n",now,t[now].fa,t[now].at-t[now].len+t[top[t[now].fa]].len,t[now].at-1,query(rt[top[t[now].fa]],1,n,t[now].at-t[now].len+t[top[t[now].fa]].len,t[now].at-1));
        // printf("%d %d %d %d\n",t[now].at,t[now].len,t[t[now].fa].at,t[t[now].fa].len);
        if(query(rt[top[t[now].fa]],1,n,t[now].at-t[now].len+t[top[t[now].fa]].len,t[now].at-1))
        {
            dp[now]=dp[t[now].fa]+1;
            top[now]=now;
        }
        else
        {
            dp[now]=dp[t[now].fa];
            top[now]=top[t[now].fa];
        }
        // printf("%d %d\n",now,dp[now]);
        for(int i=0;i<g[now].size();i++)
        {
            dfs1(g[now][i]);
        }
    }

    int print(int now,int l,int r)
    {
        if(l==r)
        {
            if(tr[now].sum) printf("%d ",l);
            return 0;
        }
        int mid=(l+r)>>1;
        if(tr[lson].sum) print(lson,l,mid);
        if(tr[rson].sum) print(rson,mid+1,r);
    }

    int solve()
    {
        for(int i=1;i<=cnt;i++)
        {
            g[t[i].fa].push_back(i);
        }
        dfs(1);
        for(int i=0;i<g[1].size();i++)
        {
            top[g[1][i]]=g[1][i];
            dp[g[1][i]]=1;
            for(int j=0;j<g[g[1][i]].size();j++)
            {
                dfs1(g[g[1][i]][j]);
            }
        }
        int ans=0;
        for(int i=1;i<=cnt;i++)
        {
            ans=max(ans,dp[i]);
        }
        printf("%d\n",ans);
    }
}sm;

int main()
{
    scanf("%d",&n);
    scanf("%s",s);
    n=strlen(s);
    for(int i=0;i<n;i++)
    {
        sm.add(s[i]-'a');
    }
    sm.solve();
    // for(int i=1;i<sm.cnt;i++)
    // {
    //     printf("%d %d\n",i,sm.t[i].at);
    // }
}


#include<bits/stdc++.h>
#define N 400040
#define lson tr[now].l
#define rson tr[now].r
using namespace std;

int n;
char s[200020];

struct SM
{
	struct point
	{
		int son[26],len,mx,at,fa;
	}t[N];

	struct tree
	{
		int sum,l,r;
	}tr[N<<4];

	int last=1,cnt=1,gg=0;
	int tot=0;
	int rt[N];
	int top[N],dp[N];
	bool sz[N];
	vector<int> g[N];

	void add(int c)
	{
		int p=last;
		int np=++cnt;
		t[np].len=t[p].len+1;
		t[np].at=++gg;
		sz[np]=1;
		while(p&&(!t[p].son[c]))
		{
			t[p].son[c]=np;
			p=t[p].fa;
		}
		if(!p) t[np].fa=1;
		else
		{
			int q=t[p].son[c],nq;
			if(t[q].len==t[p].len+1)
			{
				t[np].fa=q;
			}
			else
			{
				nq=++cnt;
				t[nq]=t[q];
				t[nq].len=t[p].len+1;
				t[np].fa=t[q].fa=nq;
				t[nq].at=t[np].at;
				while(p&&(t[p].son[c]==q))
				{
					t[p].son[c]=nq;
					p=t[p].fa;
				}
			}
		}
		last=np;
	}

	void push_up(int now)
	{
		tr[now].sum=tr[lson].sum+tr[rson].sum;
	}

	void insert(int &now,int l,int r,int pos,int val)
	{
		if(!now) now=++tot;
		if(l==r)
		{
			tr[now].sum=val;
			return ;
		}
		int mid=(l+r)>>1;
		if(pos<=mid)
		{
			insert(lson,l,mid,pos,val);
		}
		else
		{
			insert(rson,mid+1,r,pos,val);
		}
		push_up(now);
	}

	int query(int now,int l,int r,int ll,int rr)
	{
		if(ll>rr) return 0;
		if(!now) return 0;
		if(ll<=l&&r<=rr)
		{
//			printf("%d %d\n",now,tr[now].sum);
			return tr[now].sum;
		}
		int mid=(l+r)>>1;
		if(rr<=mid)
		{
			return query(lson,l,mid,ll,rr);
		}
		else
		{
			if(ll>mid)
			{
				return query(rson,mid+1,r,ll,rr);
			}
			else
			{
				return query(lson,l,mid,ll,mid)+query(rson,mid+1,r,mid+1,rr);
			}
		}
	}

	int merge(int a,int b,int l,int r)
	{
		if(!a) return b;
		if(!b) return a;
		if(l==r)
		{
			tr[a].sum=tr[b].sum;
			return 0;
		}
		int mid=(l+r)>>1;
		tr[a].l=merge(tr[a].l,tr[b].l,l,mid);
		tr[a].r=merge(tr[a].r,tr[b].r,mid+1,r);
		push_up(a);
		return a;
	}

	int dfs(int now)
	{
		if(sz[now])
		{
			insert(rt[now],1,n,t[now].at,1);
		}
		for(int i=0;i<g[now].size();i++)
		{
			dfs(g[now][i]);
			merge(rt[now],rt[g[now][i]],1,n);
//			printf("%d %d\n",now,query(rt[now],1,n,7,7));
		}
	}

	int solve()
	{
		for(int i=1;i<=cnt;i++)
		{
			g[t[i].fa].push_back(i);
		}
		for(int i=1;i<=cnt;i++)
		{
			rt[i]=i;
			tot++;
		}
		dfs(1);
		for(int i=2;i<=cnt;i++)
		{
			if(t[i].fa==1) top[i]=i,dp[i]=1;
			else
			{
				if(query(rt[top[t[i].fa]],1,n,t[i].at-t[i].len+t[t[i].fa].len,t[i].at-1))
				{
					dp[i]=dp[top[t[i].fa]]+1;
					top[i]=i;
				}
				else
				{
					dp[i]=dp[t[i].fa];
					top[i]=top[t[i].fa];
				}
			}
		}
		int ans=0;
		for(int i=1;i<=cnt;i++)
		{
			ans=max(ans,dp[i]);
		}
		printf("%d\n",ans);
	}
}sm;

int main()
{
	scanf("%d",&n);
	scanf("%s",s);
	n=strlen(s);
	for(int i=0;i<n;i++)
	{
		sm.add(s[i]-'a');
	}
	sm.solve();
}


#include <bits/stdc++.h>

const int MAXN=4e5+10;

struct Node{
	int l;
	int r;
	int cnt;
	Node* lch;
	Node* rch;
	Node(int,int);
	void Insert(int);
	int Query(int,int);
};
Node* N[MAXN];

int n;
int cnt=1;
int root=1;
int last=1;
int s[MAXN];
int prt[MAXN];
int len[MAXN];
int val[MAXN];
int pos[MAXN];
int tprt[MAXN];
char str[MAXN];
std::map<char,int> chd[MAXN];

int Extend(char);
Node* Merge(Node*,Node*);

int main(){
    scanf("%d",&n);
	scanf("%s",str+1);
	for(int i=1;i<=n;i++){
		int x=Extend(str[i]);
		N[x]->Insert(pos[x]=i);
	}
	for(int i=1;i<=cnt;i++)
		s[i]=i;
	std::stable_sort(s+1,s+cnt+1,[](int a,int b){return len[a]>len[b];});
	for(int i=1;i<cnt;i++){
		N[prt[s[i]]]=Merge(N[prt[s[i]]],N[s[i]]);
		pos[prt[s[i]]]=pos[s[i]];
	}
	int ans=0;
	for(int i=cnt-1;i>=1;i--){
		int p=s[i];
		if(prt[p]==root){
			val[p]=1;
			tprt[p]=p;
		}
		else{
			assert(N[p]->Query(pos[p],pos[p]));
			int last=tprt[prt[p]];
			int cnt=N[last]->Query(pos[p]-len[p]+(len[prt[last]]+1),pos[p]);
			if(cnt>=2){
				val[p]=val[last]+1;
				tprt[p]=p;
			}
			else{
				val[p]=val[last];
				tprt[p]=last;
			}
		}
		ans=std::max(ans,val[p]);
	}
	printf("%d\n",ans);
	return 0;
}

void Node::Insert(int x){
	++this->cnt;
	if(this->l!=this->r){
		int mid=(this->l+this->r)>>1;
		if(x<=mid){
			if(this->lch==NULL)
				this->lch=new Node(this->l,mid);
			this->lch->Insert(x);
		}
		else{
			if(this->rch==NULL)
				this->rch=new Node(mid+1,this->r);
			this->rch->Insert(x);
		}
	}
}

int Extend(char x){
	int p=last;
	int np=++cnt;
	N[last=np]=new Node(1,n);
	len[np]=len[p]+1;
	while(p&&!chd[p].count(x))
		chd[p][x]=np,p=prt[p];
	if(!p)
		prt[np]=root;
	else{
		int q=chd[p][x];
		if(len[q]==len[p]+1)
			prt[np]=q;
		else{
			int nq=++cnt;
			N[nq]=new Node(1,n);
			len[nq]=len[p]+1;
			chd[nq]=chd[q];
			prt[nq]=prt[q];
			prt[q]=nq;
			prt[np]=nq;
			while(p&&chd[p][x]==q)
				chd[p][x]=nq,p=prt[p];
		}
	}
	return np;
}

Node* Merge(Node* a,Node* b){
	if(a==NULL)
		return b;
	if(b==NULL)
		return a;
	Node* cur=new Node(a->l,b->r);
	cur->cnt=a->cnt+b->cnt;
	cur->lch=Merge(a->lch,b->lch);
	cur->rch=Merge(a->rch,b->rch);
	return cur;
}

int Node::Query(int l,int r){
	if(l<=this->l&&this->r<=r)
		return this->cnt;
	else{
		int ans=0;
		if(this->lch&&l<=this->lch->r)
			ans+=this->lch->Query(l,r);
		if(this->rch&&this->rch->l<=r)
			ans+=this->rch->Query(l,r);
		return ans;
	}
}

Node::Node(int l,int r):l(l),r(r),cnt(0),lch(NULL),rch(NULL){}


#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int maxn = 2e5 + 5;

int n;
string s;

struct Sam {
    int next[maxn << 1][26];
    int link[maxn << 1], step[maxn << 1];
    int endpos[maxn << 1], pos[maxn << 1];
    int a[maxn], b[maxn << 1];
    int subseq[maxn << 1], dp[maxn << 1];
    int sz, last, root;

    int ls[maxn * 50], rs[maxn * 50], tot;

    void update(int &i, int l, int r, int pos) {
        if (!i) {
            i = ++tot;
        }
        if (l == r) {
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) {
            update(ls[i], l, mid, pos);
        } else {
            update(rs[i], mid + 1, r, pos);
        }
    }

    int merge(int x, int y) {
        if (!x || !y) {
            return x + y;
        }
        int z = ++tot;
        ls[z] = merge(ls[x], ls[y]);
        rs[z] = merge(rs[x], rs[y]);
        return z;
    }

    int query(int i, int l, int r, int L, int R) {
        if (!i) {
            return 0;
        }
        if (l >= L && r <= R) {
            return 1;
        }
        int mid = (l + r) >> 1;
        if (L <= mid) {
            return query(ls[i], l, mid, L, R);
        }
        if (R > mid) {
            return query(rs[i], mid + 1, r, L, R);
        }
        return 0;
    }

    void init() {
        //如多次建立自动机，加入memset操作
        root = sz = last = 1;
    }

    void add(int c, int id) {
        int p = last;
        int np = ++sz;
        last = np;

        pos[np] = id;
        update(endpos[np], 1, n, id);
        step[np] = step[p] + 1;
        while (!next[p][c] && p) {
            next[p][c] = np;
            p = link[p];
        }

        if (p == 0) {
            link[np] = root;
        } else {
            int q = next[p][c];
            if (step[p] + 1 == step[q]) {
                link[np] = q;
            } else {
                int nq = ++sz;
                memcpy(next[nq], next[q], sizeof(next[q]));
                step[nq] = step[p] + 1;
                link[nq] = link[q];
                link[q] = link[np] = nq;
                while (next[p][c] == q && p) {
                    next[p][c] = nq;
                    p = link[p];
                }
            }
        }
    }

    void build() {
        init();
        for (int i = 0; i < s.length(); i++) {
            add(s[i] - 'a', i + 1);
        }
        for (int i = 1; i <= sz; i++) {
            a[step[i]]++;
        }
        for (int i = 1; i <= step[last]; i++) {
            a[i] += a[i - 1];
        }
        for (int i = 1; i <= sz; i++) {
            b[a[step[i]]--] = i;
        }
        for (int i = sz; i >= root; --i) {
            int e = b[i];
            endpos[link[e]] = merge(endpos[link[e]], endpos[e]);
        }
    }

    void solve() {
        int ans = 1;
        for (int i = root; i <= sz; ++i) {
            int e = b[i];
            if (link[e] == root) {
                dp[e] = 1, subseq[e] = e;
            } else if (query(endpos[subseq[link[e]]], 1, n, pos[e] - step[e] + step[subseq[link[e]]], pos[e] - 1)) {
                dp[e] = dp[link[e]] + 1, subseq[e] = e;
            } else {
                dp[e] = dp[link[e]], subseq[e] = subseq[link[e]];
            }
            ans = max(ans, dp[e]);
        }
        cout << ans;
    }
} sam;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> s;
    sam.build();
    sam.solve();
    return 0;
}


#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int maxn = 2e5 + 5;

int n;
string s;

struct Sam {
    int next[maxn << 1][26];
    int link[maxn << 1], step[maxn << 1];
    int endpos[maxn << 1], pos[maxn << 1];
    int a[maxn], b[maxn << 1];
    int subseq[maxn << 1], dp[maxn << 1];
    int sz, last, root;

    int ls[maxn * 50], rs[maxn * 50], tot;

    void update(int &i, int l, int r, int pos) {
        if (!i) {
            i = ++tot;
        }
        if (l == r) {
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) {
            update(ls[i], l, mid, pos);
        } else {
            update(rs[i], mid + 1, r, pos);
        }
    }

    int merge(int x, int y) {
        if (!x || !y) {
            return x + y;
        }
        int z = ++tot;
        ls[z] = merge(ls[x], ls[y]);
        rs[z] = merge(rs[x], rs[y]);
        return z;
    }

    int query(int i, int l, int r, int L, int R) {
        if (!i) {
            return 0;
        }
        if (l >= L && r <= R) {
            return 1;
        }
        int mid = (l + r) >> 1;
        int ans = 0;
        if (L <= mid) {
            ans |= query(ls[i], l, mid, L, R);
        }
        if (R > mid) {
            ans |= query(rs[i], mid + 1, r, L, R);
        }
        return ans;
    }

    void init() {
        //如多次建立自动机，加入memset操作
        root = sz = last = 1;
    }

    void add(int c, int id) {
        int p = last;
        int np = ++sz;
        last = np;

        pos[np] = id;
        update(endpos[np], 1, n, id);
        step[np] = step[p] + 1;
        while (!next[p][c] && p) {
            next[p][c] = np;
            p = link[p];
        }

        if (p == 0) {
            link[np] = root;
        } else {
            int q = next[p][c];
            if (step[p] + 1 == step[q]) {
                link[np] = q;
            } else {
                int nq = ++sz;
                memcpy(next[nq], next[q], sizeof(next[q]));
                step[nq] = step[p] + 1;
                link[nq] = link[q];
                pos[nq] = pos[q];
                link[q] = link[np] = nq;
                while (next[p][c] == q && p) {
                    next[p][c] = nq;
                    p = link[p];
                }
            }
        }
    }

    void build() {
        init();
        for (int i = 0; i < s.length(); i++) {
            add(s[i] - 'a', i + 1);
        }
        for (int i = 1; i <= sz; i++) {
            a[step[i]]++;
        }
        for (int i = 1; i <= step[last]; i++) {
            a[i] += a[i - 1];
        }
        for (int i = 1; i <= sz; i++) {
            b[a[step[i]]--] = i;
        }
        for (int i = sz; i >= root; --i) {
            int e = b[i];
            endpos[link[e]] = merge(endpos[link[e]], endpos[e]);
        }
    }

    void solve() {
        int ans = 1;
        for (int i = root; i <= sz; ++i) {
            int e = b[i];
            if (link[e] == root) {
                dp[e] = 1, subseq[e] = e;
            } else if (query(endpos[subseq[link[e]]], 1, n, pos[e] - step[e] + step[subseq[link[e]]], pos[e] - 1)) {
                dp[e] = dp[link[e]] + 1, subseq[e] = e;
            } else {
                dp[e] = dp[link[e]], subseq[e] = subseq[link[e]];
            }
            ans = max(ans, dp[e]);
        }
        cout << ans;
    }
} sam;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> s;
    sam.build();
    sam.solve();
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
int n;
#define Maxn 200010
char ch[Maxn];
int last=1,tot=1;
struct Node{
	int son[26],fa,len;
}tree[Maxn<<1];
int pos[Maxn<<1],posid=0;
int head[Maxn<<1],v[Maxn<<1],nxt[Maxn<<1],bb=0;
void add_edge(int s,int e){bb++;v[bb]=e;nxt[bb]=head[s];head[s]=bb;}
int seq[Maxn<<1],in[Maxn<<1],out[Maxn<<1],dfk=0;

void insert(char c){
	int dir=c-'a';
	int k=last,now=++tot;
	while(k&&!tree[k].son[dir])tree[k].son[dir]=now,k=tree[k].fa;
	if(!tree[k].son[dir])tree[now].fa=1;
	else{
		int q=tree[k].son[dir];
		if(tree[q].len==tree[k].len+1)tree[now].fa=q;
		else{
			int nq=++tot;
			tree[nq]=tree[q];
			tree[nq].len=tree[k].len+1;
			tree[now].fa=tree[q].fa=nq;
			while(k&&tree[k].son[dir]==q)tree[k].son[dir]=nq,k=tree[k].fa;
		}
	}
	pos[now]=++posid;tree[now].len=posid;
	last=now;
}

void dfs(int u){
	in[u]=++dfk;
	seq[dfk]=pos[u];
	for(int i=head[u];i;i=nxt[i]){
		dfs(v[i]);
		if(!pos[u])pos[u]=pos[v[i]];
	}
	out[u]=dfk;
}

namespace Seg{
	#define V 8000010
	struct Node{
		int ls,rs,sum;
	}tree[V];
	int root[Maxn<<1],cnt=0;
	void Insert(int &k,int x,int l,int r,int pos){
		k=++cnt;
		tree[k]=tree[x];
		tree[k].sum++;
		if(l==r)return;
		int mid=(l+r)>>1;
		if(pos<=mid)Insert(tree[k].ls,tree[x].ls,l,mid,pos);
		else Insert(tree[k].rs,tree[x].rs,mid+1,r,pos);
	}
	int Query(int k1,int k2,int l,int r,int L,int R){
		if(l==L&&r==R)return tree[k2].sum-tree[k1].sum;
		int mid=(l+r)>>1;
		if(R<=mid)return Query(tree[k1].ls,tree[k2].ls,l,mid,L,R);
		else if(mid<L)return Query(tree[k1].rs,tree[k2].rs,mid+1,r,L,R);
		else return Query(tree[k1].ls,tree[k2].ls,l,mid,L,mid)+Query(tree[k1].rs,tree[k2].rs,mid+1,r,mid+1,R);
	}
	int query(int u,int l,int r){
		l=max(l,1);r=min(r,n);
		if(l>r)return 0;
		return Query(root[in[u]-1],root[out[u]],1,n,l,r);
	}
}

int ans[Maxn<<1],ansanc[Maxn<<1],Ans=1;
void solve(int u,int f){
	if(u>1){
		int to=ansanc[f];
		ans[u]=max(1,ans[f]);
		if(Seg::query(to,pos[u]-(tree[u].len-tree[to].len),pos[u]-1))ans[u]=ans[f]+1;
		if(ans[u]==ans[f]+1)ansanc[u]=u;
		else ansanc[u]=to;
	}else ansanc[u]=1;
	Ans=max(Ans,ans[u]);
	for(int i=head[u];i;i=nxt[i])solve(v[i],u);
}

int main(){
	scanf("%d",&n);
	scanf("%s",ch+1);
	n=strlen(ch+1);
	for(int i=1;i<=n;++i)insert(ch[i]);
	for(int i=2;i<=tot;++i)add_edge(tree[i].fa,i);
	dfs(1);
	for(int i=1;i<=dfk;++i)
		if(seq[i])Seg::Insert(Seg::root[i],Seg::root[i-1],1,n,seq[i]);
		else Seg::root[i]=Seg::root[i-1];
	solve(1,0);
	printf("%d\n",Ans);
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
int n;
#define Maxn 200010
char ch[Maxn];
int last=1,tot=1;
struct Node{
	int son[26],fa,len;
}tree[Maxn<<1];
int pos[Maxn<<1],posid=0;
int head[Maxn<<1],v[Maxn<<1],nxt[Maxn<<1],bb=0;
void add_edge(int s,int e){bb++;v[bb]=e;nxt[bb]=head[s];head[s]=bb;}
int seq[Maxn<<1],in[Maxn<<1],out[Maxn<<1],dfk=0;

void insert(char c){
	int dir=c-'a';
	int k=last,now=++tot;
	while(k&&!tree[k].son[dir])tree[k].son[dir]=now,k=tree[k].fa;
	if(!tree[k].son[dir])tree[now].fa=1;
	else{
		int q=tree[k].son[dir];
		if(tree[q].len==tree[k].len+1)tree[now].fa=q;
		else{
			int nq=++tot;
			tree[nq]=tree[q];
			tree[nq].len=tree[k].len+1;
			tree[now].fa=tree[q].fa=nq;
			while(k&&tree[k].son[dir]==q)tree[k].son[dir]=nq,k=tree[k].fa;
		}
	}
	pos[now]=++posid;tree[now].len=posid;
	last=now;
}

void dfs(int u){
	in[u]=++dfk;
	seq[dfk]=pos[u];
	for(int i=head[u];i;i=nxt[i]){
		dfs(v[i]);
		if(!pos[u])pos[u]=pos[v[i]];
	}
	out[u]=dfk;
}

namespace Seg{
	#define V 8000010
	struct Node{
		int ls,rs,sum;
	}tree[V];
	int root[Maxn<<1],cnt=0;
	void Insert(int &k,int x,int l,int r,int pos){
		k=++cnt;
		tree[k]=tree[x];
		tree[k].sum++;
		if(l==r)return;
		int mid=(l+r)>>1;
		if(pos<=mid)Insert(tree[k].ls,tree[x].ls,l,mid,pos);
		else Insert(tree[k].rs,tree[x].rs,mid+1,r,pos);
	}
	int Query(int k1,int k2,int l,int r,int L,int R){
		if(l==L&&r==R)return tree[k2].sum-tree[k1].sum;
		int mid=(l+r)>>1;
		if(R<=mid)return Query(tree[k1].ls,tree[k2].ls,l,mid,L,R);
		else if(mid<L)return Query(tree[k1].rs,tree[k2].rs,mid+1,r,L,R);
		else return Query(tree[k1].ls,tree[k2].ls,l,mid,L,mid)+Query(tree[k1].rs,tree[k2].rs,mid+1,r,mid+1,R);
	}
	int query(int u,int l,int r){
		l=max(l,1);r=min(r,n);
		if(l>r)return 0;
		return Query(root[in[u]-1],root[out[u]],1,n,l,r);
	}
}

int ans[Maxn<<1],ansanc[Maxn<<1],Ans=1;
void solve(int u,int f){
	if(u>1){
		int to=ansanc[f];
		ans[u]=max(1,ans[f]);
		if(Seg::query(to,pos[u]-(tree[u].len-tree[to].len),pos[u]-1))ans[u]=ans[f]+1;
		if(ans[u]==ans[f]+1)ansanc[u]=u;
		else ansanc[u]=to;
	}else ansanc[u]=1;
	Ans=max(Ans,ans[u]);
	for(int i=head[u];i;i=nxt[i])solve(v[i],u);
}

int main(){
	scanf("%s",ch+1);
	n=strlen(ch+1);
	for(int i=1;i<=n;++i)insert(ch[i]);
	for(int i=2;i<=tot;++i)add_edge(tree[i].fa,i);
	dfs(1);
	for(int i=1;i<=dfk;++i)
		if(seq[i])Seg::Insert(Seg::root[i],Seg::root[i-1],1,n,seq[i]);
		else Seg::root[i]=Seg::root[i-1];
	solve(1,0);
	printf("%d\n",Ans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;

const int N = 410000;
const int P = 30;
const int M = 10000000;
int nxt[N][P], par[N], len[N], tot;
int inl[N], lst[N];
int tab[N];
int append(int p, int c)
{
    int np = ++ tot; len[np] = len[p] + 1;
    while (p && !nxt[p][c]) nxt[p][c] = np, p = par[p];
    if (!p) par[np] = 1;
    else
    {
        int q = nxt[p][c];
        if (len[q] == len[p] + 1)
            par[np] = q;
        else
        {
            int nq = ++ tot; len[nq] = len[p] + 1; lst[nq] = lst[q];
            memcpy(nxt[nq], nxt[q], sizeof nxt[q]);
            par[nq] = par[q]; par[q] = par[np] = nq;
            while (p && nxt[p][c] == q) nxt[p][c] = nq, p = par[p];
        }
    }
    return np;
}
int rt[N];
int lc[M], rc[M], cnt;
int insert(int t, int l, int r, int p)
{
    int k = ++ cnt; lc[k] = lc[t]; rc[k] = rc[t]; t = k;
    if (l == r);
    else if (p <= (l + r) / 2) lc[t] = insert(lc[t], l, (l + r) / 2, p);
    else rc[t] = insert(rc[t], (l + r) / 2 + 1, r, p);
    return t;
}
int merge(int a, int b)
{
    if (!a || !b) return a | b;
    int c = ++ cnt;
    lc[c] = merge(lc[a], lc[b]);
    rc[c] = merge(rc[a], rc[b]);
    return c;
}
int query(int t, int l, int r, int ll, int rr)
{
    if (!t) return 0;
    if (ll <= l && r <= rr) return 1;
    if (ll <= (l + r) / 2 && query(lc[t], l, (l + r) / 2, ll, rr)) return 1;
    if (rr > (l + r) / 2 && query(rc[t], (l + r) / 2 + 1, r, ll, rr)) return 1;
    return 0;
}
void dfs(int t, int l, int r)
{
    if (!t) return;
    if (l == r) cerr << l << " ";
    else
    {
        if (lc[t]) dfs(lc[t], l, (l + r) / 2);
        if (rc[t]) dfs(rc[t], (l + r) / 2 + 1, r);
    }
}
int n;
char ch[N];
queue <int> Q;
stack <int> S;
int f[N], anc[N];
int ans;
int main()
{
    ios :: sync_with_stdio(0);
    cin >> n >> ch + 1;
    tot = inl[0] = 1;
    for (int i = 1; i <= n; ++ i) lst[inl[i] = append(inl[i - 1], ch[i] - 'a')] = i;
    for (int i = 2; i <= tot; ++ i) tab[par[i]] ++;
    for (int i = 1; i <= tot; ++ i) if (!tab[i]) Q.push(i);
    while (!Q.empty())
    {
        int t = Q.front(); Q.pop(); S.push(t);
        rt[t] = insert(rt[t], 1, n, lst[t]);
        if (par[t])
        {
            rt[par[t]] = merge(rt[par[t]], rt[t]);
            if (! -- tab[par[t]]) Q.push(par[t]);
        }
    }
    while (!S.empty())
    {
        int t = S.top(); S.pop();
        if (t == 1);
        else if (par[t] == 1) f[t] = 1, anc[t] = t;
        else
        {
            int p = anc[par[t]];
            if (query(rt[p], 1, n, lst[t] - len[t] + len[p], lst[t] - 1))
            {
                f[t] = f[p] + 1;
                anc[t] = t;
            }
            else
            {
                f[t] = f[p];
                anc[t] = p;
            }
        }
        ans = max(ans, f[t]);
    }
    cout << ans << endl;
}


#include <bits/stdc++.h>
using namespace std;

const int N = 410000;
const int P = 30;
const int M = 30000000;
int nxt[N][P], par[N], len[N], tot;
int inl[N], lst[N];
int tab[N];
int append(int p, int c)
{
    int np = ++ tot; len[np] = len[p] + 1;
    while (p && !nxt[p][c]) nxt[p][c] = np, p = par[p];
    if (!p) par[np] = 1;
    else
    {
        int q = nxt[p][c];
        if (len[q] == len[p] + 1)
            par[np] = q;
        else
        {
            int nq = ++ tot; len[nq] = len[p] + 1; lst[nq] = lst[q];
            memcpy(nxt[nq], nxt[q], sizeof nxt[q]);
            par[nq] = par[q]; par[q] = par[np] = nq;
            while (p && nxt[p][c] == q) nxt[p][c] = nq, p = par[p];
        }
    }
    return np;
}
int rt[N];
int lc[M], rc[M], cnt;
int insert(int t, int l, int r, int p)
{
    int k = ++ cnt; lc[k] = lc[t]; rc[k] = rc[t]; t = k;
    if (l == r);
    else if (p <= (l + r) / 2) lc[t] = insert(lc[t], l, (l + r) / 2, p);
    else rc[t] = insert(rc[t], (l + r) / 2 + 1, r, p);
    return t;
}
int merge(int a, int b)
{
    if (!a || !b) return a | b;
    int c = ++ cnt;
    lc[c] = merge(lc[a], lc[b]);
    rc[c] = merge(rc[a], rc[b]);
    return c;
}
int query(int t, int l, int r, int ll, int rr)
{
    if (!t) return 0;
    if (ll <= l && r <= rr) return 1;
    if (ll <= (l + r) / 2 && query(lc[t], l, (l + r) / 2, ll, rr)) return 1;
    if (rr > (l + r) / 2 && query(rc[t], (l + r) / 2 + 1, r, ll, rr)) return 1;
    return 0;
}
void dfs(int t, int l, int r)
{
    if (!t) return;
    if (l == r) cerr << l << " ";
    else
    {
        if (lc[t]) dfs(lc[t], l, (l + r) / 2);
        if (rc[t]) dfs(rc[t], (l + r) / 2 + 1, r);
    }
}
int n;
char ch[N];
queue <int> Q;
stack <int> S;
int f[N], anc[N];
int ans;
int main()
{
    ios :: sync_with_stdio(0);
    cin >> n >> ch + 1;
    tot = inl[0] = 1;
    for (int i = 1; i <= n; ++ i) lst[inl[i] = append(inl[i - 1], ch[i] - 'a')] = i;
    for (int i = 2; i <= tot; ++ i) tab[par[i]] ++;
    for (int i = 1; i <= tot; ++ i) if (!tab[i]) Q.push(i);
    while (!Q.empty())
    {
        int t = Q.front(); Q.pop(); S.push(t);
        rt[t] = insert(rt[t], 1, n, lst[t]);
        if (par[t])
        {
            rt[par[t]] = merge(rt[par[t]], rt[t]);
            if (! -- tab[par[t]]) Q.push(par[t]);
        }
    }
    while (!S.empty())
    {
        int t = S.top(); S.pop();
        if (t == 1);
        else if (par[t] == 1) f[t] = 1, anc[t] = t;
        else
        {
            int p = anc[par[t]];
            if (query(rt[p], 1, n, lst[t] - len[t] + len[p], lst[t] - 1))
            {
                f[t] = f[p] + 1;
                anc[t] = t;
            }
            else
            {
                f[t] = f[p];
                anc[t] = p;
            }
        }
        ans = max(ans, f[t]);
    }
    cout << ans << endl;
}


#include <bits/stdc++.h>
using namespace std;

const int N = 300000;
const int P = 30;
const int M = 5000000;
int nxt[N][P], par[N], len[N], tot;
int inl[N], lst[N];
int tab[N];
int append(int p, int c)
{
    int np = ++ tot; len[np] = len[p] + 1;
    while (p && !nxt[p][c]) nxt[p][c] = np, p = par[p];
    if (!p) par[np] = 1;
    else
    {
        int q = nxt[p][c];
        if (len[q] == len[p] + 1)
            par[np] = q;
        else
        {
            int nq = ++ tot; len[nq] = len[p] + 1; lst[nq] = lst[q];
            memcpy(nxt[nq], nxt[q], sizeof nxt[q]);
            par[nq] = par[q]; par[q] = par[np] = nq;
            while (p && nxt[p][c] == q) nxt[p][c] = nq, p = par[p];
        }
    }
    return np;
}
int rt[N];
int lc[M], rc[M], cnt;
int insert(int t, int l, int r, int p)
{
    int k = ++ cnt; lc[k] = lc[t]; rc[k] = rc[t]; t = k;
    if (l == r);
    else if (p <= (l + r) / 2) lc[t] = insert(lc[t], l, (l + r) / 2, p);
    else rc[t] = insert(rc[t], (l + r) / 2 + 1, r, p);
    return t;
}
int merge(int a, int b)
{
    if (!a || !b) return a | b;
    int c = ++ cnt;
    lc[c] = merge(lc[a], lc[b]);
    rc[c] = merge(rc[a], rc[b]);
    return c;
}
int query(int t, int l, int r, int ll, int rr)
{
    if (!t) return 0;
    if (ll <= l && r <= rr) return 1;
    if (ll <= (l + r) / 2 && query(lc[t], l, (l + r) / 2, ll, rr)) return 1;
    if (rr > (l + r) / 2 && query(rc[t], (l + r) / 2 + 1, r, ll, rr)) return 1;
    return 0;
}
void dfs(int t, int l, int r)
{
    if (!t) return;
    if (l == r) cerr << l << " ";
    else
    {
        if (lc[t]) dfs(lc[t], l, (l + r) / 2);
        if (rc[t]) dfs(rc[t], (l + r) / 2 + 1, r);
    }
}
int n;
char ch[N];
queue <int> Q;
stack <int> S;
int f[N], anc[N];
int ans;
int main()
{
    ios :: sync_with_stdio(0);
    cin >> n >> ch + 1;
    tot = inl[0] = 1;
    for (int i = 1; i <= n; ++ i) lst[inl[i] = append(inl[i - 1], ch[i] - 'a')] = i;
    for (int i = 2; i <= tot; ++ i) tab[par[i]] ++;
    for (int i = 1; i <= tot; ++ i) if (!tab[i]) Q.push(i);
    while (!Q.empty())
    {
        int t = Q.front(); Q.pop(); S.push(t);
        rt[t] = insert(rt[t], 1, n, lst[t]);
        if (par[t])
        {
            rt[par[t]] = merge(rt[par[t]], rt[t]);
            if (! -- tab[par[t]]) Q.push(par[t]);
        }
    }
    while (!S.empty())
    {
        int t = S.top(); S.pop();
        if (t == 1);
        else if (par[t] == 1) f[t] = 1, anc[t] = t;
        else
        {
            int p = anc[par[t]];
            if (query(rt[p], 1, n, lst[t] - len[t] + len[p], lst[t] - 1))
            {
                f[t] = f[p] + 1;
                anc[t] = t;
            }
            else
            {
                f[t] = f[p];
                anc[t] = p;
            }
        }
        ans = max(ans, f[t]);
    }
    cout << ans << endl;
}


#include <bits/stdc++.h>
using namespace std;

const int N = 410000;
const int P = 25;
const int M = 10000000;
int nxt[N][P], par[N], len[N], tot;
int inl[N], lst[N];
int tab[N];
int append(int p, int c)
{
    int np = ++ tot; len[np] = len[p] + 1;
    while (p && !nxt[p][c]) nxt[p][c] = np, p = par[p];
    if (!p) par[np] = 1;
    else
    {
        int q = nxt[p][c];
        if (len[q] == len[p] + 1)
            par[np] = q;
        else
        {
            int nq = ++ tot; len[nq] = len[p] + 1; lst[nq] = lst[q];
            memcpy(nxt[nq], nxt[q], sizeof nxt[q]);
            par[nq] = par[q]; par[q] = par[np] = nq;
            while (p && nxt[p][c] == q) nxt[p][c] = nq, p = par[p];
        }
    }
    return np;
}
int rt[N];
int lc[M], rc[M], cnt;
int insert(int t, int l, int r, int p)
{
    int k = ++ cnt; lc[k] = lc[t]; rc[k] = rc[t]; t = k;
    if (l == r);
    else if (p <= (l + r) / 2) lc[t] = insert(lc[t], l, (l + r) / 2, p);
    else rc[t] = insert(rc[t], (l + r) / 2 + 1, r, p);
    return t;
}
int merge(int a, int b)
{
    if (!a || !b) return a | b;
    int c = ++ cnt;
    lc[c] = merge(lc[a], lc[b]);
    rc[c] = merge(rc[a], rc[b]);
    return c;
}
int query(int t, int l, int r, int ll, int rr)
{
    if (!t) return 0;
    if (ll <= l && r <= rr) return 1;
    if (ll <= (l + r) / 2 && query(lc[t], l, (l + r) / 2, ll, rr)) return 1;
    if (rr > (l + r) / 2 && query(rc[t], (l + r) / 2 + 1, r, ll, rr)) return 1;
    return 0;
}
void dfs(int t, int l, int r)
{
    if (!t) return;
    if (l == r) cerr << l << " ";
    else
    {
        if (lc[t]) dfs(lc[t], l, (l + r) / 2);
        if (rc[t]) dfs(rc[t], (l + r) / 2 + 1, r);
    }
}
int n;
char ch[N];
queue <int> Q;
stack <int> S;
int f[N], anc[N];
int ans;
int main()
{
    ios :: sync_with_stdio(0);
    cin >> n >> ch + 1;
    tot = inl[0] = 1;
    for (int i = 1; i <= n; ++ i) lst[inl[i] = append(inl[i - 1], ch[i] - 'a')] = i;
    for (int i = 2; i <= tot; ++ i) tab[par[i]] ++;
    for (int i = 1; i <= tot; ++ i) if (!tab[i]) Q.push(i);
    while (!Q.empty())
    {
        int t = Q.front(); Q.pop(); S.push(t);
        rt[t] = insert(rt[t], 1, n, lst[t]);
        if (par[t])
        {
            rt[par[t]] = merge(rt[par[t]], rt[t]);
            if (! -- tab[par[t]]) Q.push(par[t]);
        }
    }
    while (!S.empty())
    {
        int t = S.top(); S.pop();
        if (t == 1);
        else if (par[t] == 1) f[t] = 1, anc[t] = t;
        else
        {
            int p = anc[par[t]];
            if (query(rt[p], 1, n, lst[t] - len[t] + len[p], lst[t] - 1))
            {
                f[t] = f[p] + 1;
                anc[t] = t;
            }
            else
            {
                f[t] = f[p];
                anc[t] = p;
            }
        }
        ans = max(ans, f[t]);
    }
    cout << ans << endl;
}


#include <bits/stdc++.h>
using namespace std;

const int N = 300000;
const int P = 30;
const int M = 5000000;
int nxt[N][P], par[N], len[N], tot;
int inl[N], lst[N];
int tab[N];
int append(int p, int c)
{
    int np = ++ tot; len[np] = len[p] + 1;
    while (p && !nxt[p][c]) nxt[p][c] = np, p = par[p];
    if (!p) par[np] = 1;
    else
    {
        int q = nxt[p][c];
        if (len[q] == len[p] + 1)
            par[np] = q;
        else
        {
            int nq = ++ tot; len[nq] = len[p] + 1; lst[nq] = lst[q];
            memcpy(nxt[nq], nxt[q], sizeof nxt[q]);
            par[nq] = par[q]; par[q] = par[np] = nq;
            while (p && nxt[p][c] == q) nxt[p][c] = nq, p = par[p];
        }
    }
    return np;
}
int rt[N];
int lc[M], rc[M], cnt;
int insert(int t, int l, int r, int p)
{
    int k = ++ cnt; lc[k] = lc[t]; rc[k] = rc[t]; t = k;
    if (l == r);
    else if (p <= (l + r) / 2) lc[t] = insert(lc[t], l, (l + r) / 2, p);
    else rc[t] = insert(rc[t], (l + r) / 2 + 1, r, p);
    return t;
}
int merge(int a, int b)
{
    if (!a || !b) return a | b;
    int c = ++ cnt;
    lc[c] = merge(lc[a], lc[b]);
    rc[c] = merge(rc[a], rc[b]);
    return c;
}
int query(int t, int l, int r, int ll, int rr)
{
    if (!t) return 0;
    if (ll <= l && rr <= r) return 1;
    if (ll <= (l + r) / 2 && query(lc[t], l, (l + r) / 2, ll, rr)) return 1;
    if (rr > (l + r) / 2 && query(rc[t], (l + r) / 2 + 1, r, ll, rr)) return 1;
    return 0;
}
int n;
char ch[N];
queue <int> Q;
stack <int> S;
int f[N], anc[N];
int ans;
int main()
{
    ios :: sync_with_stdio(0);
    cin >> n >> ch + 1;
    tot = inl[0] = 1;
    for (int i = 1; i <= n; ++ i) lst[inl[i] = append(inl[i - 1], ch[i] - 'a')] = i;
    for (int i = 1; i <= tot; ++ i) tab[par[i]] ++;
    for (int i = 1; i <= tot; ++ i) if (!tab[i]) Q.push(i);
    while (!Q.empty())
    {
        int t = Q.front(); Q.pop(); S.push(t);
        rt[t] = insert(rt[t], 1, n, lst[t]);
        if (par[t])
        {
            rt[par[t]] = merge(rt[par[t]], rt[t]);
            if (! -- tab[par[t]]) Q.push(par[t]);
        }
    }
    while (!S.empty())
    {
        int t = S.top(); S.pop();
        if (t == 1);
        else if (par[t] == 1) f[t] = 1, anc[t] = t;
        else
        {
            int p = anc[par[t]];
            if (query(rt[p], 1, n, lst[t] - len[t] + len[p], lst[t] - 1))
            {
                f[t] = f[p] + 1;
                anc[t] = t;
            }
            else
            {
                f[t] = f[p];
                anc[t] = p;
            }
        }
        ans = max(ans, f[t]);
    }
    cout << ans << endl;
}


#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct SAM
{
	int child[26];
	int fail;
	int len;
}tree[400005];
struct Edge
{
	int to;
	int nxt;
}e[400005];
struct Tree
{
	#define lc tree[x].child[0]
	#define rc tree[x].child[1]
	#define mid ((l+r)>>1)
	struct Node
	{
		int child[2];
		int sum;
	}tree[10000005];
	int tot;
	int merge(int x,int y)
	{
		if(!x||!y)return x+y;
		int now=++tot;
		tree[now].sum=tree[x].sum+tree[y].sum;
		tree[now].child[0]=merge(lc,tree[y].child[0]);
		tree[now].child[1]=merge(rc,tree[y].child[1]);
		return now;
	}
	void add(int x,int l,int r,int p,int v)
	{
		tree[x].sum+=v;
		if(l==r)return;
		if(p<=mid)add(lc?lc:lc=++tot,l,mid,p,v);
		else add(rc?rc:rc=++tot,mid+1,r,p,v);
	}
	int query(int x,int l,int r,int p)
	{
		if(!tree[x].sum)return 0;
		if(l==r)return l;
		if(r<=p)
		{
			if(tree[rc].sum)return query(rc,mid+1,r,p);
			return query(lc,l,mid,p);
		}
		if(mid>=p)return query(lc,l,mid,p);
		int ans=query(rc,mid+1,r,p);
		if(ans)return ans;
		return query(lc,l,mid,p);
	}
	#undef lc
	#undef rc
	#undef mid
}T;
int n,edgenum,tot,last,root[400005],head[400005],f[400005],g[400005],st[400005],pos[400005],top;
char s[200005];
void ins(char c,int pos)
{
	int now=++tot;
	tree[now].len=tree[last].len+1;
	int node=last;
	while(node!=-1&&!tree[node].child[c-'a'])
	{
		tree[node].child[c-'a']=now;
		node=tree[node].fail;
	}
	if(node==-1)tree[now].fail=0;
	else
	{
		int cur=tree[node].child[c-'a'];
		if(tree[cur].len==tree[node].len+1)tree[now].fail=cur;
		else
		{
			int clone=++tot;
			tree[clone]=tree[cur];
			tree[clone].len=tree[node].len+1;
			while(node!=-1&&tree[node].child[c-'a']==cur)
			{
				tree[node].child[c-'a']=clone;
				node=tree[node].fail;
			}
			tree[cur].fail=tree[now].fail=clone;
		}
	}
	last=now;
	root[now]=++T.tot;
	T.add(root[now],1,n,pos,1);
}
void add(int u,int v)
{
	e[++edgenum].to=v;
	e[edgenum].nxt=head[u];
	head[u]=edgenum;
}
void dfs1(int node)
{
	for(int hd=head[node];hd;hd=e[hd].nxt)
	{
		int to=e[hd].to;
		dfs1(to);
		root[node]=T.merge(root[node],root[to]);
	}
}
void dfs2(int node)
{
	st[++top]=node;
	pos[node]=top;
	if(node)
	{
		int now=pos[g[tree[node].fail]];
		int p=T.query(root[node],1,n,n);
		while(p-T.query(root[st[now+1]],1,n,p-1)+tree[st[now+1]].len<=tree[node].len)now++;
		g[node]=st[now];
		f[node]=f[g[node]]+1;
	}
	for(int hd=head[node];hd;hd=e[hd].nxt)
	{
		int to=e[hd].to;
		dfs2(to);
	}
	top--;
}
int main()
{
	tree[0].fail=-1;
	scanf("%d",&n);
	scanf("%s",s+1);
	if(n==1)return puts("1"),0;
	for(int i=1;i<=n;i++)ins(s[i],i);
	for(int i=1;i<=tot;i++)add(tree[i].fail,i);
	dfs1(0);
	dfs2(0);
	int ans=0;
	for(int i=1;i<=tot;i++)ans=max(ans,f[i]);
	printf("%d\n",ans);
	return 0;
}


#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct SAM
{
	int child[26];
	int fail;
	int len;
}tree[400005];
struct Edge
{
	int to;
	int nxt;
}e[400005];
struct Tree
{
	#define lc tree[x].child[0]
	#define rc tree[x].child[1]
	#define mid ((l+r)>>1)
	struct Node
	{
		int child[2];
		int sum;
	}tree[10000005];
	int tot;
	int merge(int x,int y)
	{
		if(!x||!y)return x+y;
		int now=++tot;
		tree[now].sum=tree[x].sum+tree[y].sum;
		tree[now].child[0]=merge(lc,tree[y].child[0]);
		tree[now].child[1]=merge(rc,tree[y].child[1]);
		return now;
	}
	void add(int x,int l,int r,int p,int v)
	{
		tree[x].sum+=v;
		if(l==r)return;
		if(p<=mid)add(lc?lc:lc=++tot,l,mid,p,v);
		else add(rc?rc:rc=++tot,mid+1,r,p,v);
	}
	int query(int x,int l,int r,int p)
	{
		if(!tree[x].sum)return 0;
		if(l==r)return l;
		if(r<=p)
		{
			if(tree[rc].sum)return query(rc,mid+1,r,p);
			return query(lc,l,mid,p);
		}
		if(mid>=p)return query(lc,l,mid,p);
		int ans=query(rc,mid+1,r,p);
		if(ans)return ans;
		return query(lc,l,mid,p);
	}
	#undef lc
	#undef rc
	#undef mid
}T;
int n,edgenum,tot,last,root[400005],head[400005],f[400005],g[400005],st[400005],pos[400005],top;
char s[200005];
void ins(char c,int pos)
{
	int now=++tot;
	tree[now].len=tree[last].len+1;
	int node=last;
	while(node!=-1&&!tree[node].child[c-'a'])
	{
		tree[node].child[c-'a']=now;
		node=tree[node].fail;
	}
	if(node==-1)tree[now].fail=0;
	else
	{
		int cur=tree[node].child[c-'a'];
		if(tree[cur].len==tree[node].len+1)tree[now].fail=cur;
		else
		{
			int clone=++tot;
			tree[clone]=tree[cur];
			tree[clone].len=tree[node].len+1;
			while(node!=-1&&tree[node].child[c-'a']==cur)
			{
				tree[node].child[c-'a']=clone;
				node=tree[node].fail;
			}
			tree[cur].fail=tree[now].fail=clone;
		}
	}
	last=now;
	root[now]=++T.tot;
	T.add(root[now],1,n,pos,1);
}
void add(int u,int v)
{
	e[++edgenum].to=v;
	e[edgenum].nxt=head[u];
	head[u]=edgenum;
}
void dfs1(int node)
{
	for(int hd=head[node];hd;hd=e[hd].nxt)
	{
		int to=e[hd].to;
		dfs1(to);
		root[node]=T.merge(root[node],root[to]);
	}
}
void dfs2(int node)
{
	st[++top]=node;
	pos[node]=top;
	if(node)
	{
		int now=pos[g[tree[node].fail]];
		int p=T.query(root[node],1,n,n);
		while(p-T.query(root[st[now+1]],1,n,p-1)+tree[st[now+1]].len<=tree[node].len)now++;
		g[node]=st[now];
		f[node]=f[g[node]]+1;
	}
	for(int hd=head[node];hd;hd=e[hd].nxt)
	{
		int to=e[hd].to;
		dfs2(to);
	}
	top--;
}
int main()
{
	tree[0].fail=-1;
	scanf("%d",&n);
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)ins(s[i],i);
	for(int i=1;i<=tot;i++)add(tree[i].fail,i);
	dfs1(0);
	dfs2(0);
	int ans=0;
	for(int i=1;i<=tot;i++)ans=max(ans,f[i]);
	printf("%d\n",ans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
template<class T>inline void chkmax(T &a,T b){ if(a<b)a=b;}
template<class T>inline void chkmin(T &a,T b){ if(a>b)a=b;}
#define fi first
#define se second
#define mkr(a,b) make_pair(a,b)
inline int read()
{
	int sum=0,p=1;char ch=getchar();
	while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
	if(ch=='-')p=-1,ch=getchar();
	while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
	return sum*p;
}

const int maxn=2e5+20;

int n;
char str[maxn];
int x[maxn],y[maxn],v[maxn],tag[maxn],sa[maxn],rk[maxn],ht[maxn];

int st[maxn][20],lg[maxn];

inline void get_sa()
{
	int m=26;
	REP(i,1,n)tag[x[i]=str[i]-'a'+1]++;
	REP(i,1,m)tag[i]+=tag[i-1];
	DREP(i,n,1)sa[tag[x[i]]--]=i;
	for(int j=1,p=0;p<n;m=p,j<<=1)
	{
		p=0; REP(i,n-j+1,n)y[++p]=i;
		REP(i,1,n)if(sa[i]>j)y[++p]=sa[i]-j;
		REP(i,0,m)tag[i]=0;
		REP(i,1,n)tag[v[i]=x[y[i]]]++;
		REP(i,1,m)tag[i]+=tag[i-1];
		DREP(i,n,1)sa[tag[v[i]]--]=y[i];
		REP(i,1,n)v[i]=x[i];
		p=1; x[sa[1]]=1;
		REP(i,2,n)x[sa[i]]=v[sa[i]]==v[sa[i-1]] && v[sa[i]+j]==v[sa[i-1]+j] ? p :++p;
	}
	REP(i,1,n)rk[sa[i]]=i;
	int j=0;
	REP(i,1,n)
	{
		if(j)j--;
		if(rk[i]==n)continue;
		while(i+j<=n && sa[rk[i]+1]+j && str[i+j]==str[sa[rk[i]+1]+j])++j;
		ht[rk[i]]=j;
	}
	REP(i,1,n-1)st[i][0]=ht[i];
	for(int j=1;(1<<j)<n;j++)
		REP(i,1,n-(1<<j))st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
	REP(i,2,n)lg[i]=lg[i>>1]+1;
}

inline void init()
{
	n=read();
	scanf("%s",str+1);
	get_sa();
}

#define pii pair<int,int>

inline bool operator <(pii a,pii b){ return a.fi==b.fi?a.se>b.se:a.fi<b.fi;}

struct node {
	pii mx;
};
node c[maxn<<2];

#define lc (o<<1)
#define rc (o<<1|1)
#define left lc,l,mid
#define right rc,mid+1,r

void update(int ql,int qr,pii d,int o,int l,int r)
{
	if(ql<=l && r<=qr)return chkmax(c[o].mx,d),void();
	int mid=l+r>>1;
	if(ql<=mid)update(ql,qr,d,left);
	if(qr>mid)update(ql,qr,d,right);
}

pii query(int x,int o,int l,int r)
{
	if(l==r)return c[o].mx;
	int mid=l+r>>1;
	if(x<=mid)return max(c[o].mx,query(x,left));
	else return max(c[o].mx,query(x,right));
}

pii f[maxn];

inline int queryst(int l,int r)
{
	int k=lg[r-l+1];
	return min(st[l][k],st[r-(1<<k)+1][k]);
}

inline int findL(int len,int now)
{
	int l=1,r=now;
	while(l<r)
	{
		int mid=l+r>>1;
		if(queryst(mid,now-1)>=len)r=mid;
		else l=mid+1;
	}
	return l;
}

inline int findR(int len,int now)
{
	int l=now,r=n;
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(queryst(now,mid-1)>=len)l=mid;
		else r=mid-1;
	}
	return l;
}

inline void doing()
{
	f[n]=mkr(1,n);
	DREP(i,n,1)
	{
		chkmax(f[i],f[i+1]);
		chkmax(f[i],query(rk[i],1,1,n));
		int L=findL(f[i].se-i+1,rk[i]),R=findR(f[i].se-i+1,rk[i]);
		update(L,R,mkr(f[i].fi+1,f[i].se),1,1,n);
	}
	printf("%d\n",f[1].fi);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("coolshit.in","r",stdin);
	freopen("coolshit.out","w",stdout);
#endif
	init();
	doing();
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
template<class T>inline void chkmax(T &a,T b){ if(a<b)a=b;}
template<class T>inline void chkmin(T &a,T b){ if(a>b)a=b;}
#define fi first
#define se second
#define mkr(a,b) make_pair(a,b)
inline int read()
{
	int sum=0,p=1;char ch=getchar();
	while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
	if(ch=='-')p=-1,ch=getchar();
	while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
	return sum*p;
}

const int maxn=2e5+20;

int n;
char str[maxn];

struct Node {
	int s,ls,rs;
};
Node c[maxn<<7];
int ctot;

void update(int x,int d,int &o,int l,int r)
{
	if(!o)o=++ctot;
	c[o].s+=d;
	if(l==r)return;
	int mid=l+r>>1;
	if(x<=mid)update(x,d,c[o].ls,l,mid);
	else update(x,d,c[o].rs,mid+1,r);
}

int Merge(int x,int y)
{
	if(!x || !y)return x|y;
	int tmp=++ctot;
	c[tmp].s=c[x].s+c[y].s;
	c[tmp].ls=Merge(c[x].ls,c[y].ls);
	c[tmp].rs=Merge(c[x].rs,c[y].rs);
	return tmp;
}

int query(int ql,int qr,int o,int l,int r)
{
	if(!o)return 0;
	if(ql<=l && r<=qr)return c[o].s;
	int mid=l+r>>1,ans=0;
	if(ql<=mid)ans+=query(ql,qr,c[o].ls,l,mid);
	if(qr>mid)ans+=query(ql,qr,c[o].rs,mid+1,r);
	return ans;
}

struct node {
	int len,fa,ch[26],pos;
};
node t[maxn<<1];
int tot,rt[maxn<<1],last;

inline void Add(int x,int pos)
{
	int p=last,np=last=++tot;
	t[np].pos=pos; t[np].len=t[p].len+1; update(pos,1,rt[np],1,n);
	while(p && !t[p].ch[x])t[p].ch[x]=np,p=t[p].fa;
	if(!p)t[np].fa=1;
	else {
		int q=t[p].ch[x];
		if(t[q].len==t[p].len+1)t[np].fa=q;
		else {
			int nq=++tot; t[nq]=t[q];
			t[nq].len=t[p].len+1;
			t[q].fa=t[np].fa=nq;
			while(p && t[p].ch[x]==q)t[p].ch[x]=nq,p=t[p].fa;
		}
	}
}

int dp[maxn<<1];
vector<int>son[maxn<<1];

void dfs(int u)
{
	for(int v:son[u])
	{
		dfs(v);
		rt[u]=Merge(rt[u],rt[v]);
	}
}

inline void init()
{
	n=read();
	scanf("%s",str+1);
	tot=last=1;
	REP(i,1,n)Add(str[i]-'a',i);
	REP(i,2,tot)son[t[i].fa].push_back(i);
	dfs(1);
}

int ans;

void dfs1(int u,int tp)
{
	for(int v:son[u])
	{
		dp[v]=max(1,dp[tp]+(query(t[v].pos-t[v].len+t[tp].len,t[v].pos,rt[tp],1,n)>=2));
		chkmax(ans,dp[v]);
		dfs1(v,dp[v]==dp[tp]?tp:v);
	}
}

inline void doing()
{
	dfs1(1,1);
	printf("%d\n",ans);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("coolshit.in","r",stdin);
	freopen("coolshit.out","w",stdout);
#endif
	init();
	doing();
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
template<class T>inline void chkmax(T &a,T b){ if(a<b)a=b;}
template<class T>inline void chkmin(T &a,T b){ if(a>b)a=b;}
#define fi first
#define se second
#define mkr(a,b) make_pair(a,b)
inline int read()
{
	int sum=0,p=1;char ch=getchar();
	while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
	if(ch=='-')p=-1,ch=getchar();
	while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
	return sum*p;
}

const int maxn=2e5+20;
const int inf=0x3f3f3f3f;

int n;
char str[maxn];
int x[maxn],y[maxn],v[maxn],tag[maxn],sa[maxn],rk[maxn],ht[maxn];

int st[maxn][20],lg[maxn];

inline void get_sa()
{
	int m=26;
	REP(i,1,n)tag[x[i]=str[i]-'a'+1]++;
	REP(i,1,m)tag[i]+=tag[i-1];
	DREP(i,n,1)sa[tag[x[i]]--]=i;
	for(int j=1,p=0;p<n;m=p,j<<=1)
	{
		p=0; REP(i,n-j+1,n)y[++p]=i;
		REP(i,1,n)if(sa[i]>j)y[++p]=sa[i]-j;
		REP(i,0,m)tag[i]=0;
		REP(i,1,n)tag[v[i]=x[y[i]]]++;
		REP(i,1,m)tag[i]+=tag[i-1];
		DREP(i,n,1)sa[tag[v[i]]--]=y[i];
		REP(i,1,n)v[i]=x[i];
		p=1; x[sa[1]]=1;
		REP(i,2,n)x[sa[i]]=v[sa[i]]==v[sa[i-1]] && v[sa[i]+j]==v[sa[i-1]+j] ? p :++p;
	}
	REP(i,1,n)rk[sa[i]]=i;
	int j=0;
	REP(i,1,n)
	{
		if(j)j--;
		if(rk[i]==n)continue;
		while(i+j<=n && sa[rk[i]+1]+j && str[i+j]==str[sa[rk[i]+1]+j])++j;
		ht[rk[i]]=j;
	}
	REP(i,1,n-1)st[i][0]=ht[i];
	for(int j=1;(1<<j)<n;j++)
		REP(i,1,n-(1<<j))st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
	REP(i,2,n)lg[i]=lg[i>>1]+1;
}

inline void init()
{
	n=read();
	scanf("%s",str+1);
	get_sa();
}

#define pii pair<int,int>

inline bool operator <(pii a,pii b){ return a.fi==b.fi?a.se>b.se:a.fi<b.fi;}

struct node {
	pii mx;
	int mn;
};
node c[maxn<<2];

#define lc (o<<1)
#define rc (o<<1|1)
#define left lc,l,mid
#define right rc,mid+1,r

void make_tree(int o,int l,int r)
{
	c[o].mn=inf;
	if(l==r)return;
	int mid=l+r>>1;
	make_tree(left);
	make_tree(right);
}

void update(int ql,int qr,pii d,int o,int l,int r)
{
	if(ql<=l && r<=qr)return chkmax(c[o].mx,d),void();
	int mid=l+r>>1;
	if(ql<=mid)update(ql,qr,d,left);
	if(qr>mid)update(ql,qr,d,right);
}

void update1(int x,int d,int o,int l,int r)
{
	chkmin(c[o].mn,d);
	if(l==r)return;
	int mid=l+r>>1;
	if(x<=mid)update1(x,d,left);
	else update1(x,d,right);
}

int query1(int ql,int qr,int o,int l,int r)
{
	if(ql<=l && r<=qr)return c[o].mn;
	int mid=l+r>>1,ans=inf;
	if(ql<=mid)chkmin(ans,query1(ql,qr,left));
	if(qr>mid)chkmin(ans,query1(ql,qr,right));
	return ans;
}

pii query(int x,int o,int l,int r)
{
	if(l==r)return c[o].mx;
	int mid=l+r>>1;
	if(x<=mid)return max(c[o].mx,query(x,left));
	else return max(c[o].mx,query(x,right));
}

pii f[maxn];

inline int queryst(int l,int r)
{
	int k=lg[r-l+1];
	return min(st[l][k],st[r-(1<<k)+1][k]);
}

inline int findL(int len,int now)
{
	int l=1,r=now;
	while(l<r)
	{
		int mid=l+r>>1;
		if(queryst(mid,now-1)>=len)r=mid;
		else l=mid+1;
	}
	return l;
}

inline int findR(int len,int now)
{
	int l=now,r=n;
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(queryst(now,mid-1)>=len)l=mid;
		else r=mid-1;
	}
	return l;
}

inline void doing()
{
	make_tree(1,1,n);
	int ans=0;
	f[n]=mkr(1,1);
	DREP(i,n,1)
	{
		f[i]=mkr(1,1);
		chkmax(f[i],query(rk[i],1,1,n));
		int near;
		if(f[i].fi>1)
		{
			int L=findL(f[i].se,rk[i]),R=findR(f[i].se,rk[i]);
			near=query1(L,R,1,1,n)+f[i].se-1;
		}else near=i;
		int L=findL(near-i+1,rk[i]),R=findR(near-i+1,rk[i]);
		update(L,R,mkr(f[i].fi+1,near-i+1),1,1,n);
		update1(rk[i],i,1,1,n);
		chkmax(ans,f[i].fi);
	}
	printf("%d\n",ans);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("coolshit.in","r",stdin);
	freopen("coolshit.out","w",stdout);
#endif
	init();
	doing();
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
template<class T>inline void chkmax(T &a,T b){ if(a<b)a=b;}
template<class T>inline void chkmin(T &a,T b){ if(a>b)a=b;}
#define fi first
#define se second
#define mkr(a,b) make_pair(a,b)
inline int read()
{
	int sum=0,p=1;char ch=getchar();
	while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
	if(ch=='-')p=-1,ch=getchar();
	while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
	return sum*p;
}

const int maxn=2e5+20;

int n;
char str[maxn];

struct Node {
	int s,ls,rs;
};
Node c[maxn<<6];
int ctot;

void update(int x,int d,int &o,int l,int r)
{
	if(!o)o=++ctot;
	c[o].s+=d;
	if(l==r)return;
	int mid=l+r>>1;
	if(x<=mid)update(x,d,c[o].ls,l,mid);
	else update(x,d,c[o].rs,mid+1,r);
}

int Merge(int x,int y)
{
	if(!x || !y)return x|y;
	int tmp=++ctot;
	c[tmp].s=c[x].s+c[y].s;
	c[tmp].ls=Merge(c[x].ls,c[y].ls);
	c[tmp].rs=Merge(c[x].rs,c[y].rs);
	return tmp;
}

int query(int ql,int qr,int o,int l,int r)
{
	if(!o)return 0;
	if(ql<=l && r<=qr)return c[o].s;
	int mid=l+r>>1,ans=0;
	if(ql<=mid)ans+=query(ql,qr,c[o].ls,l,mid);
	if(qr>mid)ans+=query(ql,qr,c[o].rs,mid+1,r);
	return ans;
}

struct node {
	int len,fa,ch[26],pos;
};
node t[maxn<<1];
int tot,rt[maxn<<1],last;

inline void Add(int x,int pos)
{
	int p=last,np=last=++tot;
	t[np].pos=pos; t[np].len=t[p].len+1; update(pos,1,rt[np],1,n);
	while(p && !t[p].ch[x])t[p].ch[x]=np,p=t[p].fa;
	if(!p)t[np].fa=1;
	else {
		int q=t[p].ch[x];
		if(t[q].len==t[p].len+1)t[np].fa=q;
		else {
			int nq=++tot; t[nq]=t[q];
			t[nq].len=t[p].len+1;
			t[q].fa=t[np].fa=nq;
			while(p && t[p].ch[x]==q)t[p].ch[x]=nq,p=t[p].fa;
		}
	}
}

int dp[maxn<<1];
vector<int>son[maxn<<1];

void dfs(int u)
{
	for(int v:son[u])
	{
		dfs(v);
		rt[u]=Merge(rt[u],rt[v]);
	}
}

inline void init()
{
	n=read();
	scanf("%s",str+1);
	tot=last=1;
	REP(i,1,n)Add(str[i]-'a',i);
	REP(i,2,tot)son[t[i].fa].push_back(i);
	dfs(1);
}

int ans;

void dfs1(int u)
{
	for(int v:son[u])
	{
		dp[v]=max(1,dp[u]+(query(t[v].pos-t[v].len+t[u].len,t[v].pos,rt[u],1,n)>=2));
		chkmax(ans,dp[v]);
		dfs1(v);
	}
}

inline void doing()
{
	dfs1(1);
	printf("%d\n",ans);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("coolshit.in","r",stdin);
	freopen("coolshit.out","w",stdout);
#endif
	init();
	doing();
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
template<class T>inline void chkmax(T &a,T b){ if(a<b)a=b;}
template<class T>inline void chkmin(T &a,T b){ if(a>b)a=b;}
#define fi first
#define se second
#define mkr(a,b) make_pair(a,b)
inline int read()
{
	int sum=0,p=1;char ch=getchar();
	while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
	if(ch=='-')p=-1,ch=getchar();
	while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
	return sum*p;
}

const int maxn=2e5+20;

int n;
char str[maxn];

struct Node {
	int s,ls,rs;
};
Node c[maxn<<7];
int ctot;

void update(int x,int d,int &o,int l,int r)
{
	if(!o)o=++ctot;
	c[o].s+=d;
	if(l==r)return;
	int mid=l+r>>1;
	if(x<=mid)update(x,d,c[o].ls,l,mid);
	else update(x,d,c[o].rs,mid+1,r);
}

int Merge(int x,int y)
{
	if(!x || !y)return x|y;
	int tmp=++ctot;
	c[tmp].s=c[x].s+c[y].s;
	c[tmp].ls=Merge(c[x].ls,c[y].ls);
	c[tmp].rs=Merge(c[x].rs,c[y].rs);
	return tmp;
}

int query(int ql,int qr,int o,int l,int r)
{
	if(!o)return 0;
	if(ql<=l && r<=qr)return c[o].s;
	int mid=l+r>>1,ans=0;
	if(ql<=mid)ans+=query(ql,qr,c[o].ls,l,mid);
	if(qr>mid)ans+=query(ql,qr,c[o].rs,mid+1,r);
	return ans;
}

struct node {
	int len,fa,ch[26],pos;
};
node t[maxn<<1];
int tot,rt[maxn<<1],last;

inline void Add(int x,int pos)
{
	int p=last,np=last=++tot;
	t[np].pos=pos; t[np].len=t[p].len+1; update(pos,1,rt[np],1,n);
	while(p && !t[p].ch[x])t[p].ch[x]=np,p=t[p].fa;
	if(!p)t[np].fa=1;
	else {
		int q=t[p].ch[x];
		if(t[q].len==t[p].len+1)t[np].fa=q;
		else {
			int nq=++tot; t[nq]=t[q];
			t[nq].len=t[p].len+1;
			t[q].fa=t[np].fa=nq;
			while(p && t[p].ch[x]==q)t[p].ch[x]=nq,p=t[p].fa;
		}
	}
}

int dp[maxn<<1];
vector<int>son[maxn<<1];

void dfs(int u)
{
	for(int v:son[u])
	{
		dfs(v);
		rt[u]=Merge(rt[u],rt[v]);
	}
}

inline void init()
{
	n=read();
	scanf("%s",str+1);
	tot=last=1;
	REP(i,1,n)Add(str[i]-'a',i);
	REP(i,2,tot)son[t[i].fa].push_back(i);
	dfs(1);
}

int ans;

void dfs1(int u)
{
	for(int v:son[u])
	{
		dp[v]=max(1,dp[u]+(query(t[v].pos-t[v].len+t[u].len,t[v].pos,rt[u],1,n)>=2));
		chkmax(ans,dp[v]);
		dfs1(v);
	}
}

inline void doing()
{
	dfs1(1);
	printf("%d\n",ans);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("coolshit.in","r",stdin);
	freopen("coolshit.out","w",stdout);
#endif
	init();
	doing();
	return 0;
}


#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=5e5+100;
char s[maxn];
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct str
{
    int son[26];
    int ff,len,pos;
}sr[maxn];
struct node
{
    int l,r;
}no[maxn*80];
int cnt=1,last=1,tot;
int c[maxn],a[maxn],siz[maxn];
int rt[maxn];
int n;
int ans[maxn],f[maxn];
void insertt(int &x,int l,int r,int pl)
{
    if(!x)x=++tot;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pl<=mid)insertt(no[x].l,l,mid,pl);
    else insertt(no[x].r,mid+1,r,pl);
}
void merge(int &x,int &y,int l,int r)
{
    if(!x||!y){x|=y;return;}
    if(l==r)return;
    int mid=(l+r)>>1;
    merge(no[x].l,no[y].l,l,mid);
    merge(no[x].r,no[y].r,mid+1,r);
}
void extend(int c,int pos)
{
   int p=last,np=++cnt;last=np;
   sr[np].len=sr[p].len+1;sr[np].pos=pos;
   while(p&&!sr[p].son[c])sr[p].son[c]=np,p=sr[p].ff;
   if(!p)sr[np].ff=1;
   else
   {
       int q=sr[p].son[c];
       if(sr[p].len+1==sr[q].len)sr[np].ff=q;
       else
       {
           int nq=++cnt;
           sr[nq]=sr[q];
           sr[nq].len=sr[p].len+1;
           sr[q].ff=sr[np].ff=nq;
           while(p&&sr[p].son[c]==q)sr[p].son[c]=nq,p=sr[p].ff;
       }
   }
   siz[np]=1;
}
int query(int x,int l,int r,int L,int R)
{
	if(L>R)return 0;
	if(!x)return 0; if(L<=l&&r<=R)return 1;
	int mid=(l+r)>>1;
	if(L<=mid){if(query(no[x].l,l,mid,L,R))return 1;}
	if(R>mid){if(query(no[x].r,mid+1,r,L,R))return 1;}
	return 0;
}

int res=1;
int main()
{
    n=read();
    scanf("%s",s+1);
    //memset(sr,0,sizeof(sr));
    cnt=1;last=1;
    for(int i=1;i<=n;i++)extend(s[i]-'a',i),insertt(rt[last],1,n,i);
    for(int i=1;i<=cnt;i++)c[sr[i].len]++;
    for(int i=1;i<=n;i++)c[i]+=c[i-1];
    for(int i=1;i<=cnt;i++)a[c[sr[i].len]--]=i;
    for(int i=cnt;i;i--)merge(rt[sr[a[i]].ff],rt[a[i]],1,n);
    for(int i=2;i<=cnt;i++)
    {
    	int tt=a[i],fa=sr[tt].ff;
    	if(fa==1){f[tt]=tt;ans[tt]=1;continue;}
    	int flag=query(rt[f[fa]],1,n,sr[tt].pos-sr[tt].len+sr[f[fa]].len,sr[tt].pos-1);
    	if(flag)ans[tt]=ans[fa]+1,f[tt]=tt;
    	else ans[tt]=ans[fa],f[tt]=f[fa];
    	res=max(res,ans[tt]);
    }
    cout<<res<<"\n";

}


#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=4e5+100;
char s[maxn];
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct str
{
    int son[26];
    int ff,len,pos;
}sr[maxn];
struct node
{
    int l,r;
}no[maxn*40];
int cnt=1,last=1;
int siz[maxn],c[maxn],a[maxn];
int rt[maxn];
int n;
int ans[maxn],f[maxn];
void insertt(int &x,int l,int r,int pl)
{
    if(!x)x=++cnt;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pl<=mid)insertt(no[x].l,l,mid,pl);
    else insertt(no[x].r,mid+1,r,pl);
}
void merge(int &x,int &y,int l,int r)
{
    if(!x||!y){x|=y;return;}
    if(l==r)return;
    int mid=(l+r)>>1;
    merge(no[x].l,no[y].l,l,mid);
    merge(no[x].r,no[y].r,mid+1,r);
}
void extend(int c,int pos)
{
   int p=last,np=++cnt;last=np;
   sr[np].len=sr[p].len+1;sr[np].pos=pos;
   while(p&&!sr[p].son[c])sr[p].son[c]=np,p=sr[p].ff;
   if(!p)sr[np].ff=1;
   else
   {
       int q=sr[p].son[c];
       if(sr[p].len+1==sr[q].len)sr[np].ff=q;
       else
       {
           int nq=++cnt;
           sr[nq]=sr[q];
           sr[nq].len=sr[p].len+1;
           sr[q].ff=sr[np].ff=nq;
           while(p&&sr[p].son[c]==q)sr[p].son[c]=nq,p=sr[p].ff;
       }
   }
   siz[np]=1;
}
int query(int x,int l,int r,int L,int R)
{
	if(!x)return 0; if(L<=l&&r<=R)return 1;
	int mid=(l+r)>>1;
	if(L<=mid){if(query(no[x].l,l,mid,L,R))return 1;}
	if(R>mid){if(query(no[x].r,mid+1,r,L,R))return 1;}
	return 0;
}

int res=1;
int main()
{
    n=read();
    scanf("%s",s+1);
    int len=strlen(s+1);
    //memset(sr,0,sizeof(sr));
    cnt=1;last=1;
    for(int i=1;i<=len;i++)extend(s[i]-'a',i),insertt(rt[last],1,n,i);
    for(int i=1;i<=cnt;i++)c[sr[i].len]++;
    for(int i=1;i<=cnt;i++)c[i]+=c[i-1];
    for(int i=1;i<=cnt;i++)a[c[sr[i].len]--]=i;
    for(int i=cnt;i;i--)merge(rt[sr[a[i]].ff],rt[a[i]],1,n);
    for(int i=2;i<=cnt;i++)
    {
    	int tt=a[i],fa=sr[tt].ff;
    	if(fa==1){f[tt]=tt;ans[tt]=1;continue;}
    	int flag=query(rt[f[fa]],1,n,sr[tt].pos-sr[tt].len+sr[f[fa]].len,sr[tt].pos-1);
    	if(flag)ans[tt]=ans[fa]+1,f[tt]=tt;
    	else ans[tt]=ans[fa],f[tt]=f[fa];
    	res=max(res,ans[tt]);
    }
    cout<<res<<"\n";

}


#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=4e5+100;
char s[maxn];
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct str
{
    int son[26];
    int ff,len,pos;
}sr[maxn];
struct node
{
    int l,r;
}no[maxn*80];
int cnt=1,last=1;
int c[maxn],a[maxn],siz[maxn];
int rt[maxn];
int n;
int ans[maxn],f[maxn];
void insertt(int &x,int l,int r,int pl)
{
    if(!x)x=++cnt;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pl<=mid)insertt(no[x].l,l,mid,pl);
    else insertt(no[x].r,mid+1,r,pl);
}
void merge(int &x,int &y,int l,int r)
{
    if(!x||!y){x|=y;return;}
    if(l==r)return;
    int mid=(l+r)>>1;
    merge(no[x].l,no[y].l,l,mid);
    merge(no[x].r,no[y].r,mid+1,r);
}
void extend(int c,int pos)
{
   int p=last,np=++cnt;last=np;
   sr[np].len=sr[p].len+1;sr[np].pos=pos;
   while(p&&!sr[p].son[c])sr[p].son[c]=np,p=sr[p].ff;
   if(!p)sr[np].ff=1;
   else
   {
       int q=sr[p].son[c];
       if(sr[p].len+1==sr[q].len)sr[np].ff=q;
       else
       {
           int nq=++cnt;
           sr[nq]=sr[q];
           sr[nq].len=sr[p].len+1;
           sr[q].ff=sr[np].ff=nq;
           while(p&&sr[p].son[c]==q)sr[p].son[c]=nq,p=sr[p].ff;
       }
   }
   siz[np]=1;
}
int query(int x,int l,int r,int L,int R)
{
	if(!x)return 0; if(L<=l&&r<=R)return 1;
	int mid=(l+r)>>1;
	if(L<=mid){if(query(no[x].l,l,mid,L,R))return 1;}
	if(R>mid){if(query(no[x].r,mid+1,r,L,R))return 1;}
	return 0;
}

int res=1;
int main()
{
    n=read();
    scanf("%s",s+1);
    int len=strlen(s+1);
    n=len;
    //memset(sr,0,sizeof(sr));
    cnt=1;last=1;
    for(int i=1;i<=len;i++)extend(s[i]-'a',i),insertt(rt[last],1,n,i);
    for(int i=1;i<=cnt;i++)c[sr[i].len]++;
    for(int i=1;i<=cnt;i++)c[i]+=c[i-1];
    for(int i=1;i<=cnt;i++)a[c[sr[i].len]--]=i;
    for(int i=cnt;i;i--)merge(rt[sr[a[i]].ff],rt[a[i]],1,n);
    for(int i=2;i<=cnt;i++)
    {
    	int tt=a[i],fa=sr[tt].ff;
    	if(fa==1){f[tt]=tt;ans[tt]=1;continue;}
    	int flag=query(rt[f[fa]],1,n,sr[tt].pos-sr[tt].len+sr[f[fa]].len,sr[tt].pos-1);
    	if(flag)ans[tt]=ans[fa]+1,f[tt]=tt;
    	else ans[tt]=ans[fa],f[tt]=f[fa];
    	res=max(res,ans[tt]);
    }
    cout<<res<<"\n";

}


#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=5e5+100;
char s[maxn];
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct str
{
    int son[26];
    int ff,len,pos;
}sr[maxn];
struct node
{
    int l,r;
}no[maxn*80];
int cnt=1,last=1;
int c[maxn],a[maxn],siz[maxn];
int rt[maxn];
int n;
int ans[maxn],f[maxn];
void insertt(int &x,int l,int r,int pl)
{
    if(!x)x=++cnt;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pl<=mid)insertt(no[x].l,l,mid,pl);
    else insertt(no[x].r,mid+1,r,pl);
}
void merge(int &x,int &y,int l,int r)
{
    if(!x||!y){x|=y;return;}
    if(l==r)return;
    int mid=(l+r)>>1;
    merge(no[x].l,no[y].l,l,mid);
    merge(no[x].r,no[y].r,mid+1,r);
}
void extend(int c,int pos)
{
   int p=last,np=++cnt;last=np;
   sr[np].len=sr[p].len+1;sr[np].pos=pos;
   while(p&&!sr[p].son[c])sr[p].son[c]=np,p=sr[p].ff;
   if(!p)sr[np].ff=1;
   else
   {
       int q=sr[p].son[c];
       if(sr[p].len+1==sr[q].len)sr[np].ff=q;
       else
       {
           int nq=++cnt;
           sr[nq]=sr[q];
           sr[nq].len=sr[p].len+1;
           sr[q].ff=sr[np].ff=nq;
           while(p&&sr[p].son[c]==q)sr[p].son[c]=nq,p=sr[p].ff;
       }
   }
   siz[np]=1;
}
int query(int x,int l,int r,int L,int R)
{
	if(L>R)return 0;
	if(!x)return 0; if(L<=l&&r<=R)return 1;
	int mid=(l+r)>>1;
	if(L<=mid){if(query(no[x].l,l,mid,L,R))return 1;}
	if(R>mid){if(query(no[x].r,mid+1,r,L,R))return 1;}
	return 0;
}

int res=1;
int main()
{
    n=read();
    scanf("%s",s+1);
    int len=strlen(s+1);
    n=len;
    //memset(sr,0,sizeof(sr));
    cnt=1;last=1;
    for(int i=1;i<=len;i++)extend(s[i]-'a',i),insertt(rt[last],1,n,i);
    for(int i=1;i<=cnt;i++)c[sr[i].len]++;
    for(int i=1;i<=n;i++)c[i]+=c[i-1];
    for(int i=1;i<=cnt;i++)a[c[sr[i].len]--]=i;
    for(int i=cnt;i;i--)merge(rt[sr[a[i]].ff],rt[a[i]],1,n);
    for(int i=2;i<=cnt;i++)
    {
    	int tt=a[i],fa=sr[tt].ff;
    	if(fa==1){f[tt]=tt;ans[tt]=1;continue;}
    	int flag=query(rt[f[fa]],1,n,sr[tt].pos-sr[tt].len+sr[f[fa]].len,sr[tt].pos-1);
    	if(flag)ans[tt]=ans[fa]+1,f[tt]=tt;
    	else ans[tt]=ans[fa],f[tt]=f[fa];
    	res=max(res,ans[tt]);
    }
    cout<<res<<"\n";

}


#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1e6+100;
char s[maxn];
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct str
{
    int son[26];
    int ff,len,pos;
}sr[maxn];
struct node
{
    int l,r;
}no[maxn*80];
int cnt=1,last=1;
int siz[maxn],c[maxn],a[maxn];
int rt[maxn];
int n;
int ans[maxn],f[maxn];
void insertt(int &x,int l,int r,int pl)
{
    if(!x)x=++cnt;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pl<=mid)insertt(no[x].l,l,mid,pl);
    else insertt(no[x].r,mid+1,r,pl);
}
void merge(int &x,int &y,int l,int r)
{
    if(!x||!y){x|=y;return;}
    if(l==r)return;
    int mid=(l+r)>>1;
    merge(no[x].l,no[y].l,l,mid);
    merge(no[x].r,no[y].r,mid+1,r);
}
void extend(int c,int pos)
{
   int p=last,np=++cnt;last=np;
   sr[np].len=sr[p].len+1;sr[np].pos=pos;
   while(p&&!sr[p].son[c])sr[p].son[c]=np,p=sr[p].ff;
   if(!p)sr[np].ff=1;
   else
   {
       int q=sr[p].son[c];
       if(sr[p].len+1==sr[q].len)sr[np].ff=q;
       else
       {
           int nq=++cnt;
           sr[nq]=sr[q];
           sr[nq].len=sr[p].len+1;
           sr[q].ff=sr[np].ff=nq;
           while(p&&sr[p].son[c]==q)sr[p].son[c]=nq,p=sr[p].ff;
       }
   }
   siz[np]=1;
}
int query(int x,int l,int r,int L,int R)
{
	if(!x)return 0; if(L<=l&&r<=R)return 1;
	int mid=(l+r)>>1;
	if(L<=mid){if(query(no[x].l,l,mid,L,R))return 1;}
	if(R>mid){if(query(no[x].r,mid+1,r,L,R))return 1;}
	return 0;
}

int res=1;
int main()
{
    n=read();
    scanf("%s",s+1);
    int len=strlen(s+1);
    //memset(sr,0,sizeof(sr));
    cnt=1;last=1;
    for(int i=1;i<=len;i++)extend(s[i]-'a',i),insertt(rt[last],1,n,i);
    for(int i=1;i<=cnt;i++)c[sr[i].len]++;
    for(int i=1;i<=cnt;i++)c[i]+=c[i-1];
    for(int i=1;i<=cnt;i++)a[c[sr[i].len]--]=i;
    for(int i=cnt;i;i--)merge(rt[sr[a[i]].ff],rt[a[i]],1,n);
    for(int i=2;i<=cnt;i++)
    {
    	int tt=a[i],fa=sr[tt].ff;
    	if(fa==1){f[tt]=tt;ans[tt]=1;continue;}
    	int flag=query(rt[f[fa]],1,n,sr[tt].pos-sr[tt].len+sr[f[fa]].len,sr[tt].pos-1);
    	if(flag)ans[tt]=ans[fa]+1,f[tt]=tt;
    	else ans[tt]=ans[fa],f[tt]=f[fa];
    	res=max(res,ans[tt]);
    }
    cout<<res<<"\n";

}


#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=5e5+100;
char s[maxn];
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct str
{
    int son[26];
    int ff,len,pos;
}sr[maxn];
struct node
{
    int l,r;
}no[maxn*80];
int cnt=1,last=1,tot;
int c[maxn],a[maxn],siz[maxn];
int rt[maxn];
int n;
int ans[maxn],f[maxn];
void insertt(int &x,int l,int r,int pl)
{
    if(!x)x=++tot;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pl<=mid)insertt(no[x].l,l,mid,pl);
    else insertt(no[x].r,mid+1,r,pl);
}
int merge(int x,int y)
{
    if(!x||!y){return x|y;}
    int z=++tot;
    no[z].l=merge(no[x].l,no[y].l);
    no[z].r=merge(no[x].r,no[y].r);
    return z;
}
void extend(int c,int pos)
{
   int p=last,np=++cnt;last=np;
   sr[np].len=sr[p].len+1;sr[np].pos=pos;
   while(p&&!sr[p].son[c])sr[p].son[c]=np,p=sr[p].ff;
   if(!p)sr[np].ff=1;
   else
   {
       int q=sr[p].son[c];
       if(sr[p].len+1==sr[q].len)sr[np].ff=q;
       else
       {
           int nq=++cnt;
           sr[nq]=sr[q];
           sr[nq].len=sr[p].len+1;
           sr[q].ff=sr[np].ff=nq;
           while(p&&sr[p].son[c]==q)sr[p].son[c]=nq,p=sr[p].ff;
       }
   }
   siz[np]=1;
}
int query(int x,int l,int r,int L,int R)
{
	if(!x)return 0; if(L<=l&&r<=R)return 1;
	int mid=(l+r)>>1;
	if(L<=mid){if(query(no[x].l,l,mid,L,R))return 1;}
	if(R>mid){if(query(no[x].r,mid+1,r,L,R))return 1;}
	return 0;
}

int res=1;
int main()
{
    n=read();
    scanf("%s",s+1);
    //memset(sr,0,sizeof(sr));
    cnt=1;last=1;
    for(int i=1;i<=n;i++)extend(s[i]-'a',i),insertt(rt[last],1,n,i);
    for(int i=1;i<=cnt;i++)c[sr[i].len]++;
    for(int i=1;i<=n;i++)c[i]+=c[i-1];
    for(int i=1;i<=cnt;i++)a[c[sr[i].len]--]=i;
    for(int i=cnt;i;i--)rt[sr[a[i]].ff]=merge(rt[sr[a[i]].ff],rt[a[i]]);
    for(int i=2;i<=cnt;i++)
    {
    	int tt=a[i],fa=sr[tt].ff;
    	if(fa==1){f[tt]=tt;ans[tt]=1;continue;}
    	int flag=query(rt[f[fa]],1,n,sr[tt].pos-sr[tt].len+sr[f[fa]].len,sr[tt].pos-1);
    	if(flag)ans[tt]=ans[fa]+1,f[tt]=tt;
    	else ans[tt]=ans[fa],f[tt]=f[fa];
    	res=max(res,ans[tt]);
    }
    cout<<res<<"\n";

}


#include <cstdio>
#include <cstring>
#include <algorithm>

const int MN = 200005;

struct SuffixArray {
	int N, Sig, Str[MN], SA[MN], rk[MN];
	int SA2[MN], Tmp[MN];
	int Height[MN];

	inline void RSort() {
		static int Buk[MN];
		for (int i = 1; i <= Sig; ++i) Buk[i] = 0;
		for (int i = 1; i <= N; ++i) ++Buk[rk[i]];
		for (int i = 1; i <= Sig; ++i) Buk[i] += Buk[i - 1];
		for (int i = N; i >= 1; --i) SA[Buk[rk[SA2[i]]]--] = SA2[i];
	}
	inline void BuildSA() {
		for (int i = 1; i <= N; ++i) rk[i] = Str[i], SA2[i] = i;
		rk[N + 1] = 0;
		Sig = 26, RSort();
		for (int j = 1; j < N; j <<= 1) {
			int P = 0;
			for (int i = 1; i <= j; ++i) SA2[++P] = N - j + i;
			for (int i = 1; i <= N; ++i) if (SA[i] > j) SA2[++P] = SA[i] - j;
			RSort();
			Tmp[SA[1]] = P = 1;
			for (int i = 2; i <= N; ++i) {
				if (rk[SA[i]] != rk[SA[i - 1]] || rk[SA[i] + j] != rk[SA[i - 1] + j]) ++P;
				Tmp[SA[i]] = P;
			}
			for (int i = 1; i <= N; ++i) rk[i] = Tmp[i];
			Sig = P;
			if (Sig == N) break;
		}
	}
	inline void GetHeight() {
		int k = 0;
		for (int i = 1; i <= N; ++i) {
			if (rk[i] == 1) k = Height[1] = 0;
			else {
				if (k) --k;
				int j = SA[rk[i] - 1];
				while (i + k <= N && j + k <= N && Str[i + k] == Str[j + k]) ++k;
				Height[rk[i]] = k;
			}
		}
	}

	int Lg[MN], BLCP[MN][18], Bt;
	inline void InitST() {
		Lg[0] = -1;
		for (int i = 1; i <= N; ++i) Lg[i] = Lg[i >> 1] + 1, BLCP[i][0] = Height[i];
		while (2 << Bt <= N) ++Bt;
		for (int j = 1; j <= Bt; ++j)
			for (int i = 1 << j; i <= N; ++i)
				BLCP[i][j] = std::min(BLCP[i][j - 1], BLCP[i - (1 << (j - 1))][j - 1]);
	}
	inline int LCP(int x, int y) {
		if (x == y) return N + 1;
		x = rk[x], y = rk[y];
		if (x > y) std::swap(x, y);
		int j = Lg[y - x];
		return std::min(BLCP[y][j], BLCP[x + (1 << j)][j]);
	}
	inline void GetRange(int pos, int k, int &lb, int &rb) {
		int lj = 0;
		while (pos > 1 << lj && BLCP[pos][lj] >= k) ++lj;
		for (--lj, lb = pos; lj >= 0; --lj)
			if (lb > 1 << lj && BLCP[lb][lj] >= k)
				lb -= 1 << lj;
		int rj = 0;
		while (N - pos >= 1 << rj && BLCP[pos + (1 << rj)][rj] >= k) ++rj;
		for (--rj, rb = pos; rj >= 0; --rj)
			if (N - rb >= 1 << rj && BLCP[rb + (1 << rj)][rj] >= k)
				rb += 1 << rj;
	}
} SA;

struct dat {
	int k, x;
	dat() {}
	dat(int _k, int _x) : k(_k), x(_x) {}
	void z() { k = x = 0; }
	void z(int _k, int _x) { k = _k, x = _x; }
	bool t() { return k || x; }
	friend bool operator < (dat p, dat q) {
		return p.k == q.k ? p.x > q.x : p.k < q.k;
	}
};

const int MS = 1 << 19 | 7;

int N, Ans; char Str[MN];

#define li (i << 1)
#define ri (li | 1)
#define mid ((l + r) >> 1)
#define ls li, l, mid
#define rs ri, mid + 1, r
dat val[MS]; int mnv[MS];
inline void P(int i, dat v) { val[i] = std::max(val[i], v); }
inline void Pushdown(int i) { if (val[i].t()) P(li, val[i]), P(ri, val[i]), val[i].z(); }
void Build(int i, int l, int r) {
	mnv[i] = N + 1;
	if (l == r) return val[i].z(1, 0);
	Build(ls), Build(rs);
	val[i].z();
}
void Mdf1(int i, int l, int r, int a, int b, dat v) {
	if (r < a || b < l) return ;
	if (a <= l && r <= b) return P(i, v);
	Pushdown(i);
	Mdf1(ls, a, b, v), Mdf1(rs, a, b, v);
}
dat Qur1(int i, int l, int r, int p) {
	if (l == r) return val[i];
	Pushdown(i);
	return p <= mid ? Qur1(ls, p) : Qur1(rs, p);
}
void Mdf2(int i, int l, int r, int p, int v) {
	if (l == r) return mnv[i] = v, void();
	p <= mid ? Mdf2(ls, p, v) : Mdf2(rs, p, v);
	mnv[i] = std::min(mnv[li], mnv[ri]);
}
int Qur2(int i, int l, int r, int a, int b) {
	if (r < a || b < l) return N + 1;
	if (a <= l && r <= b) return mnv[i];
	return std::min(Qur2(ls, a, b), Qur2(rs, a, b));
}

int main() {
	scanf("%d%s", &N, Str + 1), SA.N = N;
	for (int i = 1; i <= N; ++i) SA.Str[i] = Str[i] - 'a' + 1;
	SA.BuildSA(), SA.GetHeight(), SA.InitST();
	Build(1, 1, N);
	for (int i = N; i >= 1; --i) {
		int lb, rb, len;
		dat now = Qur1(1, 1, N, SA.rk[i]);
		Ans = std::max(Ans, now.k);
		if (now.k > 1) {
			SA.GetRange(SA.rk[i], now.x, lb, rb);
			len = Qur2(1, 1, N, lb, rb) - i + now.x;
		} else len = 1;
		SA.GetRange(SA.rk[i], len, lb, rb);
		Mdf1(1, 1, N, lb, rb, dat(now.k + 1, len));
		Mdf2(1, 1, N, SA.rk[i], i);
	}
	printf("%d\n", Ans);
	return 0;
}


#include <cstdio>
#include <cstring>
#include <algorithm>

const int MN = 200005;

struct SuffixArray {
	int N, Sig, Str[MN], SA[MN], rk[MN];
	int SA2[MN], Tmp[MN];
	int Height[MN];

	inline void RSort() {
		static int Buk[MN];
		for (int i = 1; i <= Sig; ++i) Buk[i] = 0;
		for (int i = 1; i <= N; ++i) ++Buk[rk[i]];
		for (int i = 1; i <= Sig; ++i) Buk[i] += Buk[i - 1];
		for (int i = N; i >= 1; --i) SA[Buk[rk[SA2[i]]]--] = SA2[i];
	}
	inline void BuildSA() {
		for (int i = 1; i <= N; ++i) rk[i] = Str[i], SA2[i] = i;
		rk[N + 1] = 0;
		Sig = 26, RSort();
		for (int j = 1; j < N; j <<= 1) {
			int P = 0;
			for (int i = 1; i <= j; ++i) SA2[++P] = N - j + i;
			for (int i = 1; i <= N; ++i) if (SA[i] > j) SA2[++P] = SA[i] - j;
			RSort();
			Tmp[SA[1]] = P = 1;
			for (int i = 2; i <= N; ++i) {
				if (rk[SA[i]] != rk[SA[i - 1]] || rk[SA[i] + j] != rk[SA[i - 1] + j]) ++P;
				Tmp[SA[i]] = P;
			}
			for (int i = 1; i <= N; ++i) rk[i] = Tmp[i];
			Sig = P;
			if (Sig == N) break;
		}
	}
	inline void GetHeight() {
		int k = 0;
		for (int i = 1; i <= N; ++i) {
			if (rk[i] == 1) k = Height[1] = 0;
			else {
				if (k) --k;
				int j = SA[rk[i] - 1];
				while (i + k <= N && j + k <= N && Str[i + k] == Str[j + k]) ++k;
				Height[rk[i]] = k;
			}
		}
	}

	int Lg[MN], BLCP[MN][21], Bt;
	inline void InitST() {
		Lg[0] = -1;
		for (int i = 1; i <= N; ++i) Lg[i] = Lg[i >> 1] + 1, BLCP[i][0] = Height[i];
		while (2 << Bt <= N) ++Bt;
		for (int j = 1; j <= Bt; ++j)
			for (int i = 1 << j; i <= N; ++i)
				BLCP[i][j] = std::min(BLCP[i][j - 1], BLCP[i - (1 << (j - 1))][j - 1]);
	}
	inline int LCP(int x, int y) {
		if (x == y) return N + 1;
		x = rk[x], y = rk[y];
		if (x > y) std::swap(x, y);
		int j = Lg[y - x];
		return std::min(BLCP[y][j], BLCP[x + (1 << j)][j]);
	}
	inline void GetRange(int pos, int k, int &lb, int &rb) {
		int lj = 0;
		while (pos > 1 << lj && BLCP[pos][lj] >= k) ++lj;
		for (--lj, lb = pos; lj >= 0; --lj)
			if (lb > 1 << lj && BLCP[lb][lj] >= k)
				lb -= 1 << lj;
		int rj = 0;
		while (N - pos >= 1 << rj && BLCP[pos + (1 << rj)][rj] >= k) ++rj;
		for (--rj, rb = pos; rj >= 0; --rj)
			if (N - rb >= 1 << rj && BLCP[rb + (1 << rj)][rj] >= k)
				rb += 1 << rj;
	}
} SA;

struct dat {
	int k, x;
	dat() {}
	dat(int _k, int _x) : k(_k), x(_x) {}
	void z() { k = x = 0; }
	void z(int _k, int _x) { k = _k, x = _x; }
	bool t() { return k || x; }
	friend bool operator < (dat p, dat q) {
		return p.k == q.k ? p.x > q.x : p.k < q.k;
	}
};

int N, Ans; char Str[MN];

#define li (i << 1)
#define ri (li | 1)
#define mid ((l + r) >> 1)
#define ls li, l, mid
#define rs ri, mid + 1, r
dat val[MN]; int mnv[MN];
inline void P(int i, dat v) { val[i] = std::max(val[i], v); }
inline void Pushdown(int i) { if (val[i].t()) P(li, val[i]), P(ri, val[i]), val[i].z(); }
void Build(int i, int l, int r) {
	mnv[i] = N + 1;
	if (l == r) return val[i].z(1, 0);
	Build(ls), Build(rs);
	val[i].z();
}
void Mdf1(int i, int l, int r, int a, int b, dat v) {
	if (r < a || b < l) return ;
	if (a <= l && r <= b) return P(i, v);
	Pushdown(i);
	Mdf1(ls, a, b, v), Mdf1(rs, a, b, v);
}
dat Qur1(int i, int l, int r, int p) {
	if (l == r) return val[i];
	Pushdown(i);
	return p <= mid ? Qur1(ls, p) : Qur1(rs, p);
}
void Mdf2(int i, int l, int r, int p, int v) {
	if (l == r) return mnv[i] = v, void();
	p <= mid ? Mdf2(ls, p, v) : Mdf2(rs, p, v);
	mnv[i] = std::min(mnv[li], mnv[ri]);
}
int Qur2(int i, int l, int r, int a, int b) {
	if (r < a || b < l) return N + 1;
	if (a <= l && r <= b) return mnv[i];
	return std::min(Qur2(ls, a, b), Qur2(rs, a, b));
}

int main() {
	scanf("%d%s", &N, Str + 1), SA.N = N;
	for (int i = 1; i <= N; ++i) SA.Str[i] = Str[i] - 'a' + 1;
	SA.BuildSA(), SA.GetHeight(), SA.InitST();
	Build(1, 1, N);
	for (int i = N; i >= 1; --i) {
		int lb, rb, len;
		dat now = Qur1(1, 1, N, SA.rk[i]);
		Ans = std::max(Ans, now.k);
		if (now.k > 1) {
			SA.GetRange(SA.rk[i], now.x, lb, rb);
			len = Qur2(1, 1, N, lb, rb) - i + now.x;
		} else len = 1;
//		printf("f[%d] = <%d, %d>\n", i, now.k, len);
		SA.GetRange(SA.rk[i], len, lb, rb);
		Mdf1(1, 1, N, lb, rb, dat(now.k + 1, len));
		Mdf2(1, 1, N, SA.rk[i], i);
	}
	printf("%d\n", Ans);
	return 0;
}

/*
20
acbdbabdcacdbababcab
*/


#include <bits/stdc++.h>
using namespace std;
#define iinf 2000000000
#define linf 1000000000000000000LL
#define ulinf 10000000000000000000ull
#define MOD1 1000000007LL
#define mpr make_pair
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned long UL;
typedef unsigned short US;
typedef pair < int , int > pii;
clock_t __stt;
inline void TStart(){__stt=clock();}
inline void TReport(){printf("\nTaken Time : %.3lf sec\n",(double)(clock()-__stt)/CLOCKS_PER_SEC);}
template < typename T > T MIN(T a,T b){return a<b?a:b;}
template < typename T > T MAX(T a,T b){return a>b?a:b;}
template < typename T > T ABS(T a){return a>0?a:(-a);}
template < typename T > void UMIN(T &a,T b){if(b<a) a=b;}
template < typename T > void UMAX(T &a,T b){if(b>a) a=b;}
template < typename T > void CRE(T *&p){p=new T;p->init();}
#define SUM(x) ((x)?((x)->sum):0)
int n;
char s[200005];
struct segT{
	segT *lc,*rc;
	int sum;
	void init(){
		sum=0;
		lc=rc=0;
	}
	void add(int d,int l=0,int r=n-1){
		if(l<r){
			int M=(l+r)/2;
			if(d<=M){
				if(!lc) CRE(lc);
				lc->add(d,l,M);
			}
			else{
				if(!rc) CRE(rc);
				rc->add(d,M+1,r);
			}
			sum=SUM(lc)+SUM(rc);
		}
		else sum=1;
	}
	int qry(int L,int R,int l=0,int r=n-1){
		if(l>=L && r<=R) return sum;
		int M=(l+r)/2,ret=0;
		if(L<=M && lc) ret=lc->qry(L,R,l,M);
		if(R>M && rc) ret+=rc->qry(L,R,M+1,r);
		return ret;
	}
};
segT *merge(segT *A,segT *B,int l=0,int r=n-1){
	if(!A || !B) return A?A:B;
	segT *p;
	CRE(p);
	if(l==r){
		p->sum=A->sum+B->sum;
		return p;
	}
	p->lc=merge(A->lc,B->lc);
	p->rc=merge(A->rc,B->rc);
	p->sum=SUM(p->lc)+SUM(p->rc);
	return p;
}
struct node{
    node *nxt[26],*fa,*cor;
    int mxv,dp,id;
    segT *right;
    void init(){
    	cor=this;
    	dp=1;
        mxv=0;
        fa=0;
        CRE(right);
        memset(nxt,0,sizeof(nxt));
    }
}*last,*root;
vector < node * > ns;
void extend(int v,int d){
    node *p=last,*np=new node;
    np->init();
    ns.push_back(np);
    np->id=d;
    np->mxv=p->mxv+1;
    for(;p && !p->nxt[v];p=p->fa) p->nxt[v]=np;
    if(!p) np->fa=root;
    else{
        node *q=p->nxt[v];
        if(q->mxv==p->mxv+1){
            np->fa=q;
        }
        else{
            node *nq=new node;
            nq->init();
            ns.push_back(nq);
            nq->id=d;
            nq->mxv=p->mxv+1;
            memcpy(nq->nxt,q->nxt,sizeof(q->nxt));
            nq->fa=q->fa;
            q->fa=np->fa=nq;
            for(;p && p->nxt[v]==q;p=p->fa) p->nxt[v]=nq;
        }
    }
    last=np;
}
bool cmp(node *A,node *B){
	return A->mxv<B->mxv;
}
int res=1;
int main(){
    // inputting start
    // 数据结构记得初始化！ n，m别写反！
   	int i,j,k;
   	scanf("%d",&n);
	scanf("%s",s);
    #ifdef LOCAL
        TStart();
    #endif
    // calculation start
    // 数据结构记得初始化！ n，m别写反！
    CRE(root);
    ns.resize(1,root);
    last=root;
	for(i=0;i<n;++i){
    	extend(s[i]-'a',i);
    }
    sort(ns.begin(),ns.end(),cmp);
    for(i=(int)ns.size()-1;i>=0;--i){
    	ns[i]->right->add(ns[i]->id);
    	if(i){
    		ns[i]->fa->right=merge(ns[i]->fa->right,ns[i]->right);
    	}
    }
    for(i=1;i<(int)ns.size();++i){
    	node *F=ns[i]->fa,*T=F->cor;
    	int lb=ns[i]->id-ns[i]->mxv+T->mxv,rb=ns[i]->id;
    	if(T->right->qry(lb,rb)>=2 || T->mxv==0){
    	//	printf("go %d->%d\n",T->mxv,ns[i]->mxv);
    		ns[i]->dp=T->dp+1;
    		UMAX(res,ns[i]->dp);
    	}
    	else{
    		ns[i]->dp=T->dp;
    		ns[i]->cor=T;
    	}
    	//printf("%d\n",ns[i]->dp);
    }
    printf("%d\n",res-1);
    #ifdef LOCAL
        TReport();
    #endif
    return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define iinf 2000000000
#define linf 1000000000000000000LL
#define ulinf 10000000000000000000ull
#define MOD1 1000000007LL
#define mpr make_pair
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned long UL;
typedef unsigned short US;
typedef pair < int , int > pii;
clock_t __stt;
inline void TStart(){__stt=clock();}
inline void TReport(){printf("\nTaken Time : %.3lf sec\n",(double)(clock()-__stt)/CLOCKS_PER_SEC);}
template < typename T > T MIN(T a,T b){return a<b?a:b;}
template < typename T > T MAX(T a,T b){return a>b?a:b;}
template < typename T > T ABS(T a){return a>0?a:(-a);}
template < typename T > void UMIN(T &a,T b){if(b<a) a=b;}
template < typename T > void UMAX(T &a,T b){if(b>a) a=b;}
template < typename T > void CRE(T *&p){p=new T;p->init();}
#define SUM(x) ((x)?((x)->sum):0)
int n;
char s[200005];
struct segT{
	segT *lc,*rc;
	int sum;
	void init(){
		sum=0;
		lc=rc=0;
	}
	void add(int d,int l=0,int r=n-1){
		++sum;
		if(l<r){
			int M=(l+r)/2;
			if(d<=M){
				if(!lc) CRE(lc);
				lc->add(d,l,M);
			}
			else{
				if(!rc) CRE(rc);
				rc->add(d,M+1,r);
			}
		}
	}
	int qry(int L,int R,int l=0,int r=n-1){
		if(l>=L && r<=R) return sum;
		int M=(l+r)/2,ret=0;
		if(L<=M && lc) ret=lc->qry(L,R,l,M);
		if(R>M && rc) ret+=rc->qry(L,R,M+1,r);
		return ret;
	}
};
segT *merge(segT *A,segT *B,int l=0,int r=n-1){
	if(!A || !B) return A?A:B;
	segT *p;
	CRE(p);
	if(l==r){
		p->sum=A->sum+B->sum;
		return p;
	}
	p->lc=merge(A->lc,B->lc);
	p->rc=merge(A->rc,B->rc);
	p->sum=SUM(p->lc)+SUM(p->rc);
	return p;
}
struct node{
    node *nxt[26],*fa,*cor;
    int mxv,dp,id;
    segT *right;
    void init(){
    	cor=this;
    	dp=1;
        mxv=0;
        fa=0;
        CRE(right);
        memset(nxt,0,sizeof(nxt));
    }
}*last,*root;
vector < node * > ns;
void extend(int v,int d){
    node *p=last,*np=new node;
    np->init();
    ns.push_back(np);
    np->id=d;
    np->mxv=p->mxv+1;
    for(;p && !p->nxt[v];p=p->fa) p->nxt[v]=np;
    if(!p) np->fa=root;
    else{
        node *q=p->nxt[v];
        if(q->mxv==p->mxv+1){
            np->fa=q;
        }
        else{
            node *nq=new node;
            nq->init();
            ns.push_back(nq);
            nq->id=d;
            nq->mxv=p->mxv+1;
            memcpy(nq->nxt,q->nxt,sizeof(q->nxt));
            nq->fa=q->fa;
            q->fa=np->fa=nq;
            for(;p && p->nxt[v]==q;p=p->fa) p->nxt[v]=nq;
        }
    }
    last=np;
}
bool cmp(node *A,node *B){
	return A->mxv<B->mxv;
}
int res=1;
int main(){
    // inputting start
    // 数据结构记得初始化！ n，m别写反！
   	int i,j,k;
   	scanf("%d",&n);
	scanf("%s",s);
    #ifdef LOCAL
        TStart();
    #endif
    // calculation start
    // 数据结构记得初始化！ n，m别写反！
    CRE(root);
    ns.resize(1,root);
    last=root;
	for(i=0;i<n;++i){
    	extend(s[i]-'a',i);
    }
    sort(ns.begin(),ns.end(),cmp);
    for(i=(int)ns.size()-1;i>=0;--i){
    	ns[i]->right->add(ns[i]->id);
    	if(i){
    		ns[i]->fa->right=merge(ns[i]->fa->right,ns[i]->right);
    	}
    }
    for(i=1;i<(int)ns.size();++i){
    	node *F=ns[i]->fa,*T=F->cor;
    	int lb=ns[i]->id-ns[i]->mxv+F->mxv,rb=ns[i]->id;
    	if(T->right->qry(lb,rb)>=2){
    		ns[i]->dp=T->dp+1;
    		UMAX(res,ns[i]->dp);
    	}
    	else{
    		ns[i]->dp=T->dp;
    		ns[i]->cor=T;
    	}
    }
    printf("%d\n",res-1);
    #ifdef LOCAL
        TReport();
    #endif
    return 0;
}


#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<map>
#include<bitset>
#include<stack>
#include<queue>
#include<set>
#include<cmath>
#include<vector>

using namespace std;

#define bomb exit(1)
#define INF 1000000000
#define LINF 1000000000000000000ll
#define pprint(x) print(x),putchar(' ')
#define fprint(x) print(x),putchar('\n')
#define EE(x); struct edge { int nxt,to,w; }e[M << 1]; int head[N],cnt = x ^ 1;\
	void add(int u,int v,int w = 0) { e[++cnt].w = w,e[cnt].to = v,e[cnt].nxt = head[u];head[u] = cnt; }\
	void add_edge(int u,int v,int w = 0) { add(u,v,w),add(v,u,w * x); }
#define Mod(x) ((x) >= mod ? x - mod : x)
#define eps 0.0000000001
#define sqr(x) ((x) * (x))
#define iter iterator
#define fi first
#define se second
#define ll long long
//#define getchar() (SS == TT && (TT = (SS = BB) + fread(BB,1,1 << 15,stdin),TT == SS) ? EOF : *SS++)
//char BB[1 << 15],*SS = BB,*TT = BB;
inline ll read()
{
	ll x = 0;int f = 1;char ch = getchar();
	for(;!isdigit(ch);ch = getchar()) if(ch == '-') f = -1;
	for(;isdigit(ch);ch = getchar()) x = x * 10 + (ch ^ 48);
	return x * f;
}
void print(ll x)
{
	if(x < 0) putchar('-'),x = -x;
	if(x > 9) print(x / 10);
	putchar(x % 10 + '0');
}
const int N = 200010,mod = 1e9 + 713,base = 317;
map<pair<int,int>,vector<int> >f[N];
int n,ans,q[N];
char s[N];
int pw[N],num[N];
int Hash(int l,int r)
{
    return (num[r] - (ll)num[l - 1] * pw[r - l + 1] % mod + mod) % mod;
}
int main()
{
	n = read(),scanf("%s",s + 1);
	pw[0] = 1;
    for(int i = 1;i <= n;i++)
	{
        num[i] = ((ll)num[i - 1] * base + s[i]) % mod;
        pw[i] = (ll)pw[i - 1] * base % mod;
    }
    for(int i = 1;i <= n;i++) f[1][make_pair(s[i],1)].push_back(i);
    for(int i = 1;i <= n;i++) if(!f[i].empty())
	{
		ans = i;
        for (auto it:f[i])
        {
            int len = it.first.second;
            vector<int> a = it.second;
            int m = a.size() - 1;
            for(int j = 0;j < m;j++) q[j] = Hash(a[j] + len,a[j + 1] - 1);
            for(int l = 0,r = 1;l < m;l = r,r++)
			{
                while(r < m && q[r - 1] == q[r]) r++;
                f[i + r - l][make_pair(Hash(a[l],a[r] + len - 1),a[r] - a[l] + len)].push_back(a[l]);
            }
        }
    }
    fprint(ans);
}


#include <bits/stdc++.h>

using namespace std;

template<typename T>
vector<int> suffix_array(const T &s, int n, int alpha) {
  vector<int> a(n);
  if (~alpha) {
    vector<int> bucket(alpha);
    for (int i = 0; i < n; ++i) {
      ++bucket[s[i]];
    }
    int sum = 0;
    for (int i = 0; i < alpha; ++i) {
      int add = bucket[i];
      bucket[i] = sum;
      sum += add;
    }
    for (int i = 0; i < n; ++i) {
      a[bucket[s[i]]++] = i;
    }
  } else {
    for (int i = 0; i < n; ++i) {
      a[i] = i;
    }
    sort(a.begin(), a.end(), [&](const int &x, const int &y) {
      return s[x] < s[y];
    });
  }
  vector<int> sorted_by_second(n);
  vector<int> ptr_group(n);
  vector<int> new_group(n);
  vector<int> group(n);
  group[a[0]] = 0;
  for (int i = 1; i < n; ++i) {
    group[a[i]] = group[a[i - 1]] + (s[a[i]] != s[a[i - 1]]);
  }
  int step = 1;
  while (group[a[n - 1]] < n - 1) {
    int ptr = 0;
    for (int i = n - step; i < n; ++i) {
      sorted_by_second[ptr++] = i;
    }
    for (int i = 0; i < n; ++i) {
      if (a[i] >= step) {
        sorted_by_second[ptr++] = a[i] - step;
      }
    }
    for (int i = n - 1; ~i; --i) {
      ptr_group[group[a[i]]] = i;
    }
    for (int i = 0; i < n; ++i) {
      int x = sorted_by_second[i];
      a[ptr_group[group[x]]++] = x;
    }
    new_group[a[0]] = 0;
    for (int i = 1; i < n; ++i) {
      if (group[a[i]] != group[a[i - 1]]) {
        new_group[a[i]] = new_group[a[i - 1]] + 1;
      } else {
        int prev = a[i - 1] + step >= n ? -1 : group[a[i - 1] + step];
        int now = a[i] + step >= n ? -1 : group[a[i] + step];
        new_group[a[i]] = new_group[a[i - 1]] + (prev != now);
      }
    }
    group = new_group;
    step <<= 1;
  }
  return a;
}

template<typename T>
vector<int> build_lcp(const T &s, int n, const vector<int> &sa) {
  vector<int> pos(n);
  for (int i = 0; i < n; ++i) {
    pos[sa[i]] = i;
  }
  vector<int> lcp(n - 1);
  for (int i = 0, k = 0; i < n; ++i) {
    k = max(k - 1, 0);
    if (pos[i] == n - 1) {
      k = 0;
    } else {
      int j = sa[pos[i] + 1];
      while (i + k < n && j + k < n && s[i + k] == s[j + k]) {
        ++k;
      }
      lcp[pos[i]] = k;
    }
  }
  return lcp;
}

class segtree {
  vector<pair<int, int>> tree;
  int n;

  void modify(int x, int l, int r, int ll, int rr, int v) {
    if (ll <= l && r <= rr) {
      tree[x].first = tree[x].second = v;
    } else {
      int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
      if (ll <= y) {
        modify(x + 1, l, y, ll, rr, v);
      }
      if (rr > y) {
        modify(z, y + 1, r, ll, rr, v);
      }
      tree[x].first = min(tree[x].second, min(tree[x + 1].first, tree[z].first));
    }
  }

  int query(int x, int l, int r, int ll, int rr) {
    if (ll <= l && r <= rr) {
      return tree[x].first;
    } else {
      int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
      int ans = tree[x].second;
      if (ll <= y) {
        ans = min(ans, query(x + 1, l, y, ll, rr));
      }
      if (rr > y) {
        ans = min(ans, query(z, y + 1, r, ll, rr));
      }
      return ans;
    }
  }

 public:
  segtree(int n): n(n) {
    tree.assign(n * 2 - 1, make_pair(n, n));
  }

  void modify(int ll, int rr, int v) {
    modify(0, 0, n - 1, ll, rr, v);
  }

  int query(int ll, int rr) {
    return query(0, 0, n - 1, ll, rr);
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  string s;
  cin >> n >> s;
  if (n == 1) {
    cout << 1 << "\n";
    return 0;
  }
  vector<int> sa = suffix_array(s, n, 256);
  vector<int> lcp = build_lcp(s, n, sa);
  vector<int> pos_in_sa(n);
  for (int i = 0; i < n; ++i) {
    pos_in_sa[sa[i]] = i;
  }
  int logn = 0;
  while (1 << logn < n) {
    ++logn;
  }
  vector<vector<int>> rmq(logn, vector<int>(n - 1));
  rmq[0] = lcp;
  for (int i = 1; i < logn; ++i) {
    for (int j = 0; j + (1 << i) < n; ++j) {
      rmq[i][j] = min(rmq[i - 1][j], rmq[i - 1][j + (1 << (i - 1))]);
    }
  }
  segtree last(n);
  segtree parent(n);
  vector<int> ptr(n + 1);
  vector<int> depth(n);
  vector<int> len(n);
  vector<int> left(n);
  vector<int> right(n);
  for (int i = n - 1; ~i; --i) {
    int p = parent.query(pos_in_sa[i], pos_in_sa[i]);
    if (p == n) {
      depth[i] = 1;
      len[i] = 1;
    } else {
      depth[i] = depth[p] + 1;
      len[i] = last.query(left[p], right[p] - 1) + len[p] - i;
    }
    int l = pos_in_sa[i], r = pos_in_sa[i];
    for (int j = logn - 1; ~j; --j) {
      if (l - (1 << j) >= 0 && rmq[j][l - (1 << j)] >= len[i]) {
        l -= 1 << j;
      }
      if (r + (1 << j) < n && rmq[j][r] >= len[i]) {
        r += 1 << j;
      }
    }
    left[i] = ptr[p];
    right[i] = left[i] + (r - l + 1);
    ptr[i] = left[i] + 1;
    ptr[p] = right[i];
    last.modify(left[i], left[i], i);
    parent.modify(l, r, i);
  }
  cout << *max_element(depth.begin(), depth.end()) << "\n";
  return 0;
}


#include<bits/stdc++.h>
#define N 220000
int Len,n;
int len[N<<1],fa[N<<1],son[N<<1][26];
int size,last,root,pos[N<<1],rt[N*40],lc[N*40],rc[N*40];
int a[N<<1],c[N<<1],T,sum[N*40],ans[N<<1],ANS;
int f[N<<1];
char s[N];
void Init(){root=size=last=1;}
void Extend(int c,int wz){
    int end=++size,tmp=last;
    len[end]=len[tmp]+1;pos[end]=wz;
    while(tmp&&!son[tmp][c]){
        son[tmp][c]=end;
        tmp=fa[tmp];
    }
    if(!tmp) fa[end]=root;
    else{
        int ne=son[tmp][c];
        if(len[tmp]+1==len[ne]) fa[end]=ne;
        else{
            int np=++size;
            memcpy(son[np],son[ne],sizeof(son[ne]));
            len[np]=len[tmp]+1;
            fa[np]=fa[ne];
            fa[end]=fa[ne]=np;
            pos[np]=pos[ne];
            while(tmp&&son[tmp][c]==ne){
                son[tmp][c]=np;
                tmp=fa[tmp];
          	  }
       	}
     }
    last=end;
}
void Radixsort(){
    for(int i=1;i<=size;i++) c[len[i]]++;
    for(int i=1;i<=Len;i++) c[i]+=c[i-1];
    for(int i=size;i>=1;i--) a[c[len[i]]--]=i;
}
void insert(int &i, int l, int r, int x){
    if(r<l) return ;
    i = ++T;
    if(l == r) {
        sum[i]++ ;
        //num[i]=1LL*x;
        return ;
    }
    int mid = (l + r)/2 ;
    if(x<=mid) insert(lc[i], l, mid, x) ;
    if(x>mid) insert(rc[i], mid + 1, r, x) ;
    //clac(i);
    sum[i]=sum[lc[i]]+sum[rc[i]];
}
int merge(int x, int y) {
    if(!x) return y;
    if(!y) return x;
    //sum[x]+=sum[y];
    int i=++T;
    sum[i]=sum[x]+sum[y];
    lc[i] = merge(lc[x], lc[y]) ;
    rc[i] = merge(rc[x], rc[y]) ;
    //sum[x]=sum[lc[x]]+sum[rc[x]];
    return i;
}
bool query(int x,int l,int r,int ls,int rs){
	if (!x)return 0;
	if (ls<=l&&r<=rs)return sum[x]>0;
	int mid=(l+r)/2;
	if (ls<=mid){
		if (query(lc[x],l,mid,ls,rs))return 1;
	}
	if (mid<rs){
		if (query(rc[x],mid+1,r,ls,rs))return 1;
	}
	return 0;
	//return query(lc[x],l,mid,ls,rs)|query(rc[x],mid+1,r,ls,rs);
}
int main(){
	scanf("%d",&n);
	scanf("%s",s);
	Len=n;
	Init();
	for (int i=0;i<n;i++)Extend(s[i]-'a',i+1),insert(rt[last],1,n,i+1);
	Radixsort();
	//printf("%d\n",size);
	for (int i=size;i;i--){
		int p=a[i];
		rt[fa[p]]=merge(rt[fa[p]],rt[p]);
	}
	ANS=1;
	for (int i=2;i<=size;i++){
		int p=a[i];
		if (fa[p]==1){
			ans[p]=1;
			f[p]=p;
			continue;
		}
		//printf("%d %d %d\n",fa[p],p,i);
		bool flag=query(rt[f[fa[p]]],1,n,pos[p]-len[p]+len[f[fa[p]]],pos[p]-1);
		if (flag){
			ans[p]=ans[fa[p]]+1;
			f[p]=p;
		}else{
			ans[p]=ans[fa[p]];
			f[p]=f[fa[p]];
		}
		ANS=std::max(ANS,ans[p]);
	}
	printf("%d",ANS);
	return 0;
}