#include <bits/stdc++.h>

using namespace std;

const int N = 4e5 + 7;

int rt[N << 1], n;

struct Segment_Tree {

#define ls(x) (T[x].ch[0])
#define rs(x) (T[x].ch[1])
#define mid ((l + r) >> 1)

	int cnt;

	struct node { int ch[2], s; } T[N << 7];

	void update(int &bh, int l, int r, int x) {
		if (!bh) bh = ++ cnt;
		++ T[bh].s;
		if (l == r) return;
		if (x <= mid) update(ls(bh), l, mid, x);
		else update(rs(bh), mid + 1, r, x);
	}

	int merge(int x, int y) {
		if (!x || !y) return x + y;
		int t = ++ cnt;
		T[t].s = T[x].s + T[y].s;
		ls(t) = merge(ls(x), ls(y));
		rs(t) = merge(rs(x), rs(y));
		return t;
	}

	bool query(int bh, int l, int r, int L, int R) {
		if (!T[bh].s) return false;
		if (L <= l && r <= R) return T[bh].s;
		if (L <= mid && query(ls(bh), l, mid, L, R)) return 1;
		if (R > mid && query(rs(bh), mid + 1, r, L, R)) return 1;
		return false;
	}

} SGT;

struct Suffix_Automaton {

	int lst, cnt;

	struct node { int trans[26], link, len, pos; } T[N << 1];

	Suffix_Automaton() { lst = cnt = 1; }

	void extend(int pos, int to) {
		int p = lst, now = lst = ++ cnt;
		T[now].len = T[p].len + 1, T[now].pos = pos;
		for(; p && !T[p].trans[to]; p = T[p].link)
			T[p].trans[to] = now;
		if (!p) T[now].link = 1;
		else {
			int x = T[p].trans[to], y;
			if (T[x].len == T[p].len + 1) T[now].link = x;
			else {
				T[y = ++ cnt] = T[x], T[y].len = T[p].len + 1;
				for (; p && T[p].trans[to] == x; p = T[p].link)
					T[p].trans[to] = y;
				T[now].link = T[x].link = y;
			}
		}
		SGT.update(rt[now], 1, n, pos);
	}

	int tp[N], tong[N], f[N], ans;

	void Solve() {
		for (int i = 1; i <= cnt; ++ i) ++ tong[T[i].len];
		for (int i = 1; i <= cnt; ++ i) tong[i] += tong[i - 1];
		for (int i = 1; i <= cnt; ++ i) tp[tong[T[i].len] --] = i;
		for (int i = cnt; i > 1; -- i) rt[T[tp[i]].link] = SGT.merge(rt[T[tp[i]].link], rt[tp[i]]);
		for (int i = 2; i <= cnt; ++ i) {
			int pos = T[tp[i]].pos, fa = T[tp[i]].link;
			f[tp[i]] = 1;
			if (fa ^ 1 && SGT.query(rt[fa], 1, n, pos - T[tp[i]].len + T[fa].len, pos - 1))
				f[tp[i]] = f[fa] + 1;
			ans = max(ans, f[tp[i]]);
		}
		printf("%d\n", ans);
	}

} SAM;

int main() {

	char s[N];

	scanf("%d%s", &n, s + 1);
	for (int i = 1; i <= n; ++ i)
		SAM.extend(i, s[i] - 'a');
	SAM.Solve();

	return 0;
}


#include <bits/stdc++.h>

using namespace std;

const int N = 4e5 + 7;

int rt[N], n;

struct Segment_Tree {

#define ls(x) (T[x].ch[0])
#define rs(x) (T[x].ch[1])
#define mid ((l + r) >> 1)

    int cnt;

    struct node { int ch[2], s; } T[N << 5];

    void update(int &bh, int l, int r, int x) {
        if (!bh) bh = ++ cnt;
        ++ T[bh].s;
        if (l == r) return;
        if (x <= mid) update(ls(bh), l, mid, x);
        else update(rs(bh), mid + 1, r, x);
    }

    int merge(int x, int y) {
        if (!x || !y) return x + y;
        int t = ++ cnt;
        T[t].s = T[x].s + T[y].s;
        ls(t) = merge(ls(x), ls(y));
        rs(t) = merge(rs(x), rs(y));
        return t;
    }

    bool query(int bh, int l, int r, int L, int R) {
        if (!T[bh].s) return false;
        if (L <= l && r <= R) return 1;
        if (L <= mid && query(ls(bh), l, mid, L, R)) return 1;
        if (R > mid && query(rs(bh), mid + 1, r, L, R)) return 1;
        return false;
    }

} SGT;

struct Suffix_Automaton {

    int lst, cnt;

    struct node { int trans[26], link, len, pos; } T[N];

    Suffix_Automaton() { lst = cnt = 1; }

    void extend(int pos, int to) {
        int p = lst, now = lst = ++ cnt;
        T[now].len = T[p].len + 1, T[now].pos = pos;
        for(; p && !T[p].trans[to]; p = T[p].link)
            T[p].trans[to] = now;
        if (!p) T[now].link = 1;
        else {
            int x = T[p].trans[to], y;
            if (T[x].len == T[p].len + 1) T[now].link = x;
            else {
                T[y = ++ cnt] = T[x], T[y].len = T[p].len + 1;
                for (; p && T[p].trans[to] == x; p = T[p].link)
                    T[p].trans[to] = y;
                T[now].link = T[x].link = y;
            }
        }
        SGT.update(rt[now], 1, n, pos);
    }

    int tp[N], tong[N], f[N], Belong[N], ans;

    void Solve() {
        for (int i = 1; i <= cnt; ++ i) ++ tong[T[i].len];
        for (int i = 1; i <= cnt; ++ i) tong[i] += tong[i - 1];
        for (int i = 1; i <= cnt; ++ i) tp[tong[T[i].len] --] = i;
        for (int i = cnt; i > 1; -- i) rt[T[tp[i]].link] = SGT.merge(rt[T[tp[i]].link], rt[tp[i]]);
        for (int i = 2; i <= cnt; ++ i) {
            int pos = T[tp[i]].pos, fa = T[tp[i]].link;
            if (fa == 1) f[Belong[tp[i]] = tp[i]] = 1;
            else if (SGT.query(rt[Belong[fa]], 1, n, pos - T[tp[i]].len + T[Belong[fa]].len, pos - 1))
                f[tp[i]] = f[fa] + 1, Belong[tp[i]] = tp[i];
            else f[tp[i]] = f[fa], Belong[tp[i]] = Belong[fa];
            ans = max(ans, f[tp[i]]);
        }
        printf("%d\n", ans);
    }

} SAM;

int main() {

    char s[N];

    scanf("%d%s", &n, s + 1);
    for (int i = 1; i <= n; ++ i)
        SAM.extend(i, s[i] - 'a');
    SAM.Solve();

    return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define ri register int
namespace io {
	const int SIZE = (1 << 21) + 1;
	char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
	// getchar
	#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
	// print the remaining part
	inline void flush () {
		fwrite (obuf, 1, oS - obuf, stdout);
		oS = obuf;
	}
	// putchar
	inline void putc (char x) {
		*oS ++ = x;
		if (oS == oT) flush ();
	}
	// input a signed integer
	template <class I>
	inline void gi (I &x) {
		for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
		for (x = 0; c <= '9' && c >= '0'; c = gc()) x = (x << 1) + (x << 3) + (c & 15); x *= f;
	}
	// print a signed integer
	template <class I>
	inline void print (I x) {
		if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
		while (x) qu[++ qr] = x % 10 + '0',  x /= 10;
		while (qr) putc (qu[qr --]);
	}
	// no need to call flush at the end manually!
	struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi;
using io :: putc;
using io :: print;
const int N=2e5+5,S=N<<1,M=20,SIZE=S*40;
int n,last,size,trans[S][26],len[S],fa[S],f[S][M],dp[S],cnt[N],q[S],pos[S];
char s[N];
int segsize,rt[S],res[SIZE],lc[SIZE],rc[SIZE];
int tot,head[S],nxt[S],adj[S];
inline int merge(ri x,ri y){
	if(!x||!y) return x|y;
	ri cur=++segsize;
	res[cur]=res[x]+res[y];
	lc[cur]=merge(lc[x],lc[y]);
	rc[cur]=merge(rc[x],rc[y]);
	return cur;
}
inline void upd(int &x,ri l,ri r,ri p){
	if(!x) x=++segsize;
	++res[x];
	if(l==r) return;
	ri mid=l+r>>1;
	p<=mid?upd(lc[x],l,mid,p):upd(rc[x],mid+1,r,p);
}
inline int query(ri x,ri l,ri r,ri ql,ri qr){
	if(!x) return 0;
	if(ql<=l&&r<=qr)
		return res[x];
	ri mid=l+r>>1,s=0;
	if(ql<=mid) s= query(lc[x],l,mid,ql,qr);
	if(qr> mid) s+=query(rc[x],mid+1,r,ql,qr);
	return s;
}
inline void extend(ri c,ri i){
	ri cur=++size,p=last;
	upd(rt[cur],1,n,i);
	len[cur]=len[p]+1;
	pos[cur]=i;
	for(;p&&!trans[p][c];p=fa[p]) trans[p][c]=cur;
	if(!p) fa[cur]=1;
	else{
		ri q=trans[p][c];
		if(len[p]+1==len[q]) fa[cur]=q;
		else{
			ri clone=++size;
			len[clone]=len[p]+1;
			memcpy(trans[clone],trans[q],sizeof(trans[q]));
			fa[clone]=fa[q];
			fa[cur]=fa[q]=clone;
			for(;trans[p][c]==q;p=fa[p]) trans[p][c]=clone;
		}
	}
	last=cur;
}
int ans;
inline void dfs(ri x){
	ri y,now,i;
	if(pos[x]==1) dp[x]=1,ans=max(ans,dp[x]);
	else if(x!=1){
		now=x;
		for(i=17;i>=0;--i)
			if((y=f[now][i])&&!query(rt[y],1,n,max(1,pos[x]-len[x]+len[y]),pos[x]-1))
				now=y;
		now=fa[now];
		dp[x]=dp[now]+1;
		ans=max(ans,dp[x]);
	}
	for(i=head[x];i;i=nxt[i])
		dfs(adj[i]);
}
inline void addedge(ri x,ri y){
	nxt[++tot]=head[x];
	adj[head[x]=tot]=y;
}
int main(){
	ri i,j;
	scanf("%d%s",&n,s+1);
	n=strlen(s+1);
	last=size=1;
	for(i=1;i<=n;++i) extend(s[i]-'a',i);
	for(i=1;i<=size;++i) f[i][0]=fa[i];
	for(j=1;j<=17;++j)
		for(i=1;i<=size;++i)
			f[i][j]=f[f[i][j-1]][j-1];
	for(i=1;i<=size;++i) ++cnt[len[i]];
	for(i=1;i<=n;++i) cnt[i]+=cnt[i-1];
	for(i=1;i<=size;++i) q[cnt[len[i]]--]=i;
	for(i=size;i>1;--i) j=q[i],rt[fa[j]]=merge(rt[fa[j]],rt[j]);
//	for(i=1;i<=size;++i)
//		printf("%d %d %d %d\n",i,pos[i],res[rt[i]],fa[i]);
	for(i=2;i<=size;++i) addedge(fa[i],i);
	dfs(1);
	printf("%d\n",ans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define ri register int
namespace io {
	const int SIZE = (1 << 21) + 1;
	char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
	// getchar
	#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
	// print the remaining part
	inline void flush () {
		fwrite (obuf, 1, oS - obuf, stdout);
		oS = obuf;
	}
	// putchar
	inline void putc (char x) {
		*oS ++ = x;
		if (oS == oT) flush ();
	}
	// input a signed integer
	template <class I>
	inline void gi (I &x) {
		for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
		for (x = 0; c <= '9' && c >= '0'; c = gc()) x = (x << 1) + (x << 3) + (c & 15); x *= f;
	}
	// print a signed integer
	template <class I>
	inline void print (I x) {
		if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
		while (x) qu[++ qr] = x % 10 + '0',  x /= 10;
		while (qr) putc (qu[qr --]);
	}
	// no need to call flush at the end manually!
	struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi;
using io :: putc;
using io :: print;
const int N=2e5+5,S=N<<1,SIZE=S*100;
int n,last,size,trans[S][26],len[S],fa[S],dp[S],cnt[N],q[S],pos[S],f[S];
char s[N];
int segsize,rt[S],res[SIZE],lc[SIZE],rc[SIZE];
inline int merge(ri x,ri y){
	if(!x||!y) return x|y;
	ri cur=++segsize;
	res[cur]=res[x]+res[y];
	lc[cur]=merge(lc[x],lc[y]);
	rc[cur]=merge(rc[x],rc[y]);
	return cur;
}
inline void upd(int &x,ri l,ri r,ri p){
	if(!x) x=++segsize;
	++res[x];
	if(l==r) return;
	ri mid=l+r>>1;
	p<=mid?upd(lc[x],l,mid,p):upd(rc[x],mid+1,r,p);
}
inline int query(ri x,ri l,ri r,ri ql,ri qr){
	if(!x) return 0;
	if(ql<=l&&r<=qr)
		return res[x];
	ri mid=l+r>>1,s=0;
	if(ql<=mid) s= query(lc[x],l,mid,ql,qr);
	if(qr> mid) s+=query(rc[x],mid+1,r,ql,qr);
	return s;
}
inline void extend(ri c,ri i){
	ri cur=++size,p=last;
	upd(rt[cur],1,n,i);
	len[cur]=len[p]+1;
	pos[cur]=i;
	for(;p&&!trans[p][c];p=fa[p]) trans[p][c]=cur;
	if(!p) fa[cur]=1;
	else{
		ri q=trans[p][c];
		if(len[p]+1==len[q]) fa[cur]=q;
		else{
			ri clone=++size;
			len[clone]=len[p]+1;
			memcpy(trans[clone],trans[q],sizeof(trans[q]));
			fa[clone]=fa[q];
			fa[cur]=fa[q]=clone;
			for(;trans[p][c]==q;p=fa[p]) trans[p][c]=clone;
		}
	}
	last=cur;
}
int main(){
	ri i,j,x,ans;
	scanf("%d%s",&n,s+1);
	n=strlen(s+1);
	last=size=1;
	for(i=1;i<=n;++i) extend(s[i]-'a',i);
	for(i=1;i<=size;++i) ++cnt[len[i]];
	for(i=1;i<=n;++i) cnt[i]+=cnt[i-1];
	for(i=1;i<=size;++i) q[cnt[len[i]]--]=i;
	for(i=size;i>1;--i){
		j=q[i];
		rt[fa[j]]=merge(rt[fa[j]],rt[j]);
		if(!pos[fa[j]])
			pos[fa[j]]=pos[j];
	}
	ans=0;
	for(i=2;i<=size;++i){
		x=q[i];
		j=fa[x];
		if(j==1)
			f[x]=x,
			dp[x]=1;
		else if(query(rt[f[j]],1,n,max(1,pos[x]-len[x]+len[f[j]]),pos[x]-1))
			f[x]=x,
			dp[x]=dp[j]+1;
		else
			f[x]=f[j],
			dp[x]=dp[j];
		ans=max(ans,dp[x]);
	}
	printf("%d\n",ans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define ri register int
namespace io {
	const int SIZE = (1 << 21) + 1;
	char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
	// getchar
	#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
	// print the remaining part
	inline void flush () {
		fwrite (obuf, 1, oS - obuf, stdout);
		oS = obuf;
	}
	// putchar
	inline void putc (char x) {
		*oS ++ = x;
		if (oS == oT) flush ();
	}
	// input a signed integer
	template <class I>
	inline void gi (I &x) {
		for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
		for (x = 0; c <= '9' && c >= '0'; c = gc()) x = (x << 1) + (x << 3) + (c & 15); x *= f;
	}
	// print a signed integer
	template <class I>
	inline void print (I x) {
		if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
		while (x) qu[++ qr] = x % 10 + '0',  x /= 10;
		while (qr) putc (qu[qr --]);
	}
	// no need to call flush at the end manually!
	struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi;
using io :: putc;
using io :: print;
const int N=2e5+5,S=N<<1,M=20,SIZE=S*40;
int n,last,size,trans[S][26],len[S],fa[S],f[S][M],dp[S],cnt[N],q[S],pos[S];
char s[N];
int segsize,rt[S],res[SIZE],lc[SIZE],rc[SIZE];
int tot,head[S],nxt[S],adj[S];
inline int merge(ri x,ri y){
	if(!x||!y) return x|y;
	ri cur=++segsize;
	res[cur]=res[x]+res[y];
	lc[cur]=merge(lc[x],lc[y]);
	rc[cur]=merge(rc[x],rc[y]);
	return cur;
}
inline void upd(int &x,ri l,ri r,ri p){
	if(!x) x=++segsize;
	++res[x];
	if(l==r) return;
	ri mid=l+r>>1;
	p<=mid?upd(lc[x],l,mid,p):upd(rc[x],mid+1,r,p);
}
inline int query(ri x,ri l,ri r,ri ql,ri qr){
	if(!x) return 0;
	if(ql<=l&&r<=qr)
		return res[x];
	ri mid=l+r>>1,s=0;
	if(ql<=mid) s= query(lc[x],l,mid,ql,qr);
	if(qr> mid) s+=query(rc[x],mid+1,r,ql,qr);
	return s;
}
inline void extend(ri c,ri i){
	ri cur=++size,p=last;
	upd(rt[cur],1,n,i);
	len[cur]=len[p]+1;
	pos[cur]=i;
	for(;p&&!trans[p][c];p=fa[p]) trans[p][c]=cur;
	if(!p) fa[cur]=1;
	else{
		ri q=trans[p][c];
		if(len[p]+1==len[q]) fa[cur]=q;
		else{
			ri clone=++size;
			len[clone]=len[p]+1;
			memcpy(trans[clone],trans[q],sizeof(trans[q]));
			fa[clone]=fa[q];
			fa[cur]=fa[q]=clone;
			for(;trans[p][c]==q;p=fa[p]) trans[p][c]=clone;
		}
	}
	last=cur;
}
int ans;
inline void dfs(ri x){
	ri y,now,i;
	if(pos[x]==1) dp[x]=1,ans=max(ans,dp[x]);
	else if(x==1) dp[x]=0;
	else{
		now=x;
		for(i=17;i>=0;--i)
			if((y=f[now][i])&&!query(rt[y],1,n,max(1,pos[x]-len[x]+len[y]),pos[x]-1))
				now=y;
		now=fa[now];
		dp[x]=dp[now]+1;
		ans=max(ans,dp[x]);
	}
	for(i=head[x];i;i=nxt[i])
		dfs(adj[i]);
}
inline void addedge(ri x,ri y){
	nxt[++tot]=head[x];
	adj[head[x]=tot]=y;
}
int main(){
	ri i,j;
	scanf("%d%s",&n,s+1);
	n=strlen(s+1);
	last=size=1;
	for(i=1;i<=n;++i) extend(s[i]-'a',i);
	for(i=1;i<=size;++i) f[i][0]=fa[i];
	for(j=1;j<=17;++j)
		for(i=1;i<=size;++i)
			f[i][j]=f[f[i][j-1]][j-1];
	for(i=1;i<=size;++i) ++cnt[len[i]];
	for(i=1;i<=n;++i) cnt[i]+=cnt[i-1];
	for(i=1;i<=size;++i) q[cnt[len[i]]--]=i;
	for(i=size;i>1;--i){
		j=q[i];
		rt[fa[j]]=merge(rt[fa[j]],rt[j]);
		if(!pos[fa[j]])
			pos[fa[j]]=pos[j];
	}
	for(i=2;i<=size;++i) addedge(fa[i],i);
	dfs(1);
	printf("%d\n",ans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define ri register int
namespace io {
	const int SIZE = (1 << 21) + 1;
	char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
	// getchar
	#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
	// print the remaining part
	inline void flush () {
		fwrite (obuf, 1, oS - obuf, stdout);
		oS = obuf;
	}
	// putchar
	inline void putc (char x) {
		*oS ++ = x;
		if (oS == oT) flush ();
	}
	// input a signed integer
	template <class I>
	inline void gi (I &x) {
		for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
		for (x = 0; c <= '9' && c >= '0'; c = gc()) x = (x << 1) + (x << 3) + (c & 15); x *= f;
	}
	// print a signed integer
	template <class I>
	inline void print (I x) {
		if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
		while (x) qu[++ qr] = x % 10 + '0',  x /= 10;
		while (qr) putc (qu[qr --]);
	}
	// no need to call flush at the end manually!
	struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi;
using io :: putc;
using io :: print;
const int N=2e5+5,S=N<<1,SIZE=S*80;
int n,last,size,trans[S][26],len[S],fa[S],dp[S],cnt[N],q[S],pos[S],f[S];
char s[N];
int segsize,rt[S],res[SIZE],lc[SIZE],rc[SIZE];
inline int merge(ri x,ri y){
	if(!x||!y) return x|y;
	ri cur=++segsize;
	res[cur]=res[x]+res[y];
	lc[cur]=merge(lc[x],lc[y]);
	rc[cur]=merge(rc[x],rc[y]);
	return cur;
}
inline void upd(int &x,ri l,ri r,ri p){
	if(!x) x=++segsize;
	++res[x];
	if(l==r) return;
	ri mid=l+r>>1;
	p<=mid?upd(lc[x],l,mid,p):upd(rc[x],mid+1,r,p);
}
inline int query(ri x,ri l,ri r,ri ql,ri qr){
	if(!x) return 0;
	if(ql<=l&&r<=qr)
		return res[x];
	ri mid=l+r>>1,s=0;
	if(ql<=mid) s= query(lc[x],l,mid,ql,qr);
	if(qr> mid) s+=query(rc[x],mid+1,r,ql,qr);
	return s;
}
inline void extend(ri c,ri i){
	ri cur=++size,p=last;
	upd(rt[cur],1,n,i);
	len[cur]=len[p]+1;
	pos[cur]=i;
	for(;p&&!trans[p][c];p=fa[p]) trans[p][c]=cur;
	if(!p) fa[cur]=1;
	else{
		ri q=trans[p][c];
		if(len[p]+1==len[q]) fa[cur]=q;
		else{
			ri clone=++size;
			len[clone]=len[p]+1;
			memcpy(trans[clone],trans[q],sizeof(trans[q]));
			fa[clone]=fa[q];
			fa[cur]=fa[q]=clone;
			for(;trans[p][c]==q;p=fa[p]) trans[p][c]=clone;
		}
	}
	last=cur;
}
int main(){
	ri i,j,x,ans;
	scanf("%d%s",&n,s+1);
	n=strlen(s+1);
	last=size=1;
	for(i=1;i<=n;++i) extend(s[i]-'a',i);
	for(i=1;i<=size;++i) ++cnt[len[i]];
	for(i=1;i<=n;++i) cnt[i]+=cnt[i-1];
	for(i=1;i<=size;++i) q[cnt[len[i]]--]=i;
	for(i=size;i>1;--i){
		j=q[i];
		rt[fa[j]]=merge(rt[fa[j]],rt[j]);
		if(!pos[fa[j]])
			pos[fa[j]]=pos[j];
	}
	ans=0;
	for(i=2;i<=size;++i){
		x=q[i];
		j=fa[x];
		if(j==1)
			f[x]=x,
			dp[x]=1;
		else if(query(rt[f[j]],1,n,max(1,pos[x]-len[x]+len[f[j]]),pos[x]-1))
			f[x]=x,
			dp[x]=dp[j]+1;
		else
			f[x]=f[j],
			dp[x]=dp[j];
		ans=max(ans,dp[x]);
	}
	printf("%d\n",ans);
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define ri register int
namespace io {
	const int SIZE = (1 << 21) + 1;
	char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
	// getchar
	#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
	// print the remaining part
	inline void flush () {
		fwrite (obuf, 1, oS - obuf, stdout);
		oS = obuf;
	}
	// putchar
	inline void putc (char x) {
		*oS ++ = x;
		if (oS == oT) flush ();
	}
	// input a signed integer
	template <class I>
	inline void gi (I &x) {
		for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
		for (x = 0; c <= '9' && c >= '0'; c = gc()) x = (x << 1) + (x << 3) + (c & 15); x *= f;
	}
	// print a signed integer
	template <class I>
	inline void print (I x) {
		if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
		while (x) qu[++ qr] = x % 10 + '0',  x /= 10;
		while (qr) putc (qu[qr --]);
	}
	// no need to call flush at the end manually!
	struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi;
using io :: putc;
using io :: print;
const int N=2e5+5,S=N<<1,M=20,SIZE=S*40;
int n,last,size,trans[S][26],len[S],fa[S],f[S][M],dp[S],cnt[N],q[S],pos[S];
char s[N];
int segsize,rt[S],res[SIZE],lc[SIZE],rc[SIZE];
int tot,head[S],nxt[S],adj[S];
inline int merge(ri x,ri y){
	if(!x||!y) return x|y;
	ri cur=++segsize;
	res[cur]=res[x]+res[y];
	lc[cur]=merge(lc[x],lc[y]);
	rc[cur]=merge(rc[x],rc[y]);
	return cur;
}
inline void upd(int &x,ri l,ri r,ri p){
	if(!x) x=++segsize;
	++res[x];
	if(l==r) return;
	ri mid=l+r>>1;
	p<=mid?upd(lc[x],l,mid,p):upd(rc[x],mid+1,r,p);
}
inline int query(ri x,ri l,ri r,ri ql,ri qr){
	if(!x) return 0;
	if(ql<=l&&r<=qr)
		return res[x];
	ri mid=l+r>>1,s=0;
	if(ql<=mid) s= query(lc[x],l,mid,ql,qr);
	if(qr> mid) s+=query(rc[x],mid+1,r,ql,qr);
	return s;
}
inline void extend(ri c,ri i){
	ri cur=++size,p=last;
	upd(rt[cur],1,n,i);
	len[cur]=len[p]+1;
	pos[cur]=i;
	for(;p&&!trans[p][c];p=fa[p]) trans[p][c]=cur;
	if(!p) fa[cur]=1;
	else{
		ri q=trans[p][c];
		if(len[p]+1==len[q]) fa[cur]=q;
		else{
			ri clone=++size;
			len[clone]=len[p]+1;
			memcpy(trans[clone],trans[q],sizeof(trans[q]));
			fa[clone]=fa[q];
			fa[cur]=fa[q]=clone;
			for(;trans[p][c]==q;p=fa[p]) trans[p][c]=clone;
		}
	}
	last=cur;
}
int ans;
inline void dfs(ri x){
	ri y,now,i;
	if(pos[x]==1) dp[x]=1;
	else if(x!=1){
		now=x;
		for(i=17;i>=0;--i)
			if((y=f[now][i])&&!query(rt[y],1,n,max(1,pos[x]-len[x]+len[y]),pos[x]-1))
				now=y;
		now=fa[now];
		dp[x]=dp[now]+1;
		ans=max(ans,dp[x]);
	}
	for(i=head[x];i;i=nxt[i])
		dfs(adj[i]);
}
inline void addedge(ri x,ri y){
	nxt[++tot]=head[x];
	adj[head[x]=tot]=y;
}
int main(){
	ri i,j;
	scanf("%d%s",&n,s+1);
	n=strlen(s+1);
	last=size=1;
	for(i=1;i<=n;++i) extend(s[i]-'a',i);
	for(i=1;i<=size;++i) f[i][0]=fa[i];
	for(j=1;j<=17;++j)
		for(i=1;i<=size;++i)
			f[i][j]=f[f[i][j-1]][j-1];
	for(i=1;i<=size;++i) ++cnt[len[i]];
	for(i=1;i<=n;++i) cnt[i]+=cnt[i-1];
	for(i=1;i<=size;++i) q[cnt[len[i]]--]=i;
	for(i=size;i>1;--i) j=q[i],rt[fa[j]]=merge(rt[fa[j]],rt[j]);
//	for(i=1;i<=size;++i)
//		printf("%d %d %d %d\n",i,pos[i],res[rt[i]],fa[i]);
	for(i=2;i<=size;++i) addedge(fa[i],i);
	dfs(1);
	printf("%d\n",ans);
	return 0;
}


#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 800006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , m;
int A[MAXN];
char ch[MAXN];

struct SAM {
	int son[MAXN][26] , par[MAXN] , len[MAXN] , cnt , lst;
	void init( ) {
		lst = cnt = 1;
	}
	int rt[MAXN] , cn , ls[MAXN * 10] , rs[MAXN * 10] , T[MAXN * 10];
	void add( int& rt , int l , int r , int p ) {
		if( !rt ) rt = ++ cn;
		++ T[rt];
		if( l == r ) return;
		int m = l + r >> 1;
		if( p <= m ) add( ls[rt] , l , m , p );
		else add( rs[rt] , m + 1 , r , p );
	}
	vi G[MAXN];
	void addall( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) G[par[i]].pb( i );
	}
	void ins( int x , int i ) {
		int cur = ++ cnt , p = lst;
		len[cur] = len[lst] + 1;
		while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
		if( !p ) par[cur] = 1;
		else {
			int q = son[p][x];
			if( len[q] == len[p] + 1 ) par[cur] = q;
			else {
				int ct = ++ cnt;
				len[ct] = len[p] + 1 , par[ct] = par[q];
				memcpy( son[ct] , son[q] , sizeof son[ct] );
				par[q] = par[cur] = ct;
				for( ; son[p][x] == q ; p = par[p] ) son[p][x] = ct;
			}
		}
		lst = cur;
		add( rt[cur] , 1 , n , i );
	}
	int merge( int u , int v , int l , int r ) {
		if( !u || !v ) return u ^ v;
		int cur = ++ cn , m = l + r >> 1;
		T[cur] = T[u] + T[v];
		if( l == r ) return cur;
		ls[cur] = merge( ls[u] , ls[v] , l , m ) , rs[cur] = merge( rs[u] , rs[v] , m + 1 , r );
		return cur;
	}
	int que( int rt , int l , int r , int L , int R ) {
		if( !rt ) return 0;
		if( L <= l && R >= r ) return 1;
		int m = l + r >> 1 , ans = 0;
		if( L <= m ) ans |= que( ls[rt] , l , m , L , R );
		if( R > m ) ans |= que( rs[rt] , m + 1 , r , L , R );
	}
	int yusland( int rt , int l , int r ) {
		if( l == r ) return l;
		int m = l + r >> 1;
		if( ls[rt] ) return yusland( ls[rt] , l , m );
		if( rs[rt] ) return yusland( rs[rt] , m + 1 , r );
	}
	int g[MAXN][20];
	void dfs( int u ) {
		for( int v : G[u] ) {
			g[v][0] = u;
			for( int k = 1 ; k < 19 ; ++ k ) if( g[g[v][k-1]][k-1] ) g[u][k] = g[g[v][k-1]][k-1]; else break;
			dfs( v ) , rt[u] = merge( rt[u] , rt[v] , 1 , n );
		}
	}
	int jump( int u , int l , int r ) {
		for( int k = 18 ; k >= 0 ; -- k ) {
			int v = g[u][k];
			if( !v || que( rt[v] , 1 , n , l + len[v] - 1 , r ) ) continue;
			u = g[u][k];
		}
		return par[u];
	}
	int dp[MAXN] , ans = 0;
	void sol( int u ) {
		if( u != 1 ) {
			int ps = yusland( rt[u] , 1 , n ) , v = jump( u , ps - len[u] + 1 , ps );
			dp[u] = dp[v] + 1;
		}
		ans = max( ans , dp[u] );
		for( int v : G[u] )
			sol( v );
	}
	void fuck( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) cout << i << ' ' << dp[i] << endl;
	}
} S ;

void solve() {
	cin >> n;
	scanf("%s",ch + 1);
	S.init();
	rep( i , 1 , n ) S.ins( ch[i] - 'a' , i );
	S.addall( );
	S.dfs( 1 );
	S.sol( 1 );
	cout << S.ans << endl;
}

signed main() {
//    int T;cin >> T;while( T-- ) solve();
    solve();
}


#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 800006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , m;
int A[MAXN];
char ch[MAXN];

struct SAM {
	int son[MAXN][26] , par[MAXN] , len[MAXN] , cnt , lst;
	void init( ) {
		lst = cnt = 1;
	}
	int rt[MAXN] , cn , ls[MAXN * 10] , rs[MAXN * 10] , T[MAXN * 10];
	void add( int& rt , int l , int r , int p ) {
		if( !rt ) rt = ++ cn;
		++ T[rt];
		if( l == r ) return;
		int m = l + r >> 1;
		if( p <= m ) add( ls[rt] , l , m , p );
		else add( rs[rt] , m + 1 , r , p );
	}
	vi G[MAXN];
	void addall( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) G[par[i]].pb( i );
	}
	void ins( int x , int i ) {
		int cur = ++ cnt , p = lst;
		len[cur] = len[lst] + 1;
		while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
		if( !p ) par[cur] = 1;
		else {
			int q = son[p][x];
			if( len[q] == len[p] + 1 ) par[cur] = q;
			else {
				int ct = ++ cnt;
				len[ct] = len[p] + 1 , par[ct] = par[q];
				memcpy( son[ct] , son[q] , sizeof son[ct] );
				par[q] = par[cur] = ct;
				for( ; son[p][x] == q ; p = par[p] ) son[p][x] = ct;
			}
		}
		lst = cur;
		add( rt[cur] , 1 , n , i );
	}
	int merge( int u , int v , int l , int r ) {
		if( !u || !v ) return u ^ v;
		int cur = ++ cn , m = l + r >> 1;
		T[cur] = T[u] + T[v];
		if( l == r ) return cur;
		ls[cur] = merge( ls[u] , ls[v] , l , m ) , rs[cur] = merge( rs[u] , rs[v] , m + 1 , r );
		return cur;
	}
	int que( int rt , int l , int r , int L , int R ) {
		if( !rt ) return 0;
		if( L <= l && R >= r ) return 1;
		int m = l + r >> 1 , ans = 0;
		if( L <= m ) ans |= que( ls[rt] , l , m , L , R );
		if( R > m ) ans |= que( rs[rt] , m + 1 , r , L , R );
		return ans;
	}
	int yusland( int rt , int l , int r ) {
		if( l == r ) return l;
		int m = l + r >> 1;
		if( rs[rt] ) return yusland( rs[rt] , m + 1 , r );
		if( ls[rt] ) return yusland( ls[rt] , l , m );
	}
	int g[MAXN][20];
	void dfs( int u ) {
		for( int v : G[u] ) {
			g[v][0] = u;
			for( int k = 1 ; k < 19 ; ++ k ) if( g[g[v][k-1]][k-1] ) g[v][k] = g[g[v][k-1]][k-1]; else break;
			dfs( v ) , rt[u] = merge( rt[u] , rt[v] , 1 , n );
		}
	}
	int jump( int u , int l , int r ) {
		for( int k = 18 ; k >= 0 ; -- k ) {
			int v = g[u][k];
			if( !v || que( rt[v] , 1 , n , l + len[v] - 1 , r ) ) continue;
			u = g[u][k];
		}
		return par[u];
	}
	int dp[MAXN] , ans = 0;
	void sol( int u ) {
		if( u != 1 ) {
			int ps = yusland( rt[u] , 1 , n ) , v = jump( u , ps - len[u] + 1 , ps );
			dp[u] = dp[v] + 1;
		}
		ans = max( ans , dp[u] );
		for( int v : G[u] )
			sol( v );
	}
	void fuck( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) cout << i << ' ' << dp[i] << endl;
	}
} S ;

void solve() {
	cin >> n;
	scanf("%s",ch + 1);
	S.init();
	rep( i , 1 , n ) S.ins( ch[i] - 'a' , i );
	S.addall( );
	S.dfs( 1 );
	S.sol( 1 );
	cout << S.ans << endl;
//	S.fuck();
}

signed main() {
//    int T;cin >> T;while( T-- ) solve();
    solve();
}


#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 800006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , m;
int A[MAXN];
char ch[MAXN];

struct SAM {
	int son[MAXN][26] , par[MAXN] , len[MAXN] , cnt , lst;
	void init( ) {
		lst = cnt = 1;
	}
	int rt[MAXN] , cn , ls[MAXN * 10] , rs[MAXN * 10] , T[MAXN * 10];
	void add( int& rt , int l , int r , int p ) {
		if( !rt ) rt = ++ cn;
		++ T[rt];
		if( l == r ) return;
		int m = l + r >> 1;
		if( p <= m ) add( ls[rt] , l , m , p );
		else add( rs[rt] , m + 1 , r , p );
	}
	vi G[MAXN];
	void addall( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) G[par[i]].pb( i );
	}
	void ins( int x , int i ) {
		int cur = ++ cnt , p = lst;
		len[cur] = len[lst] + 1;
		while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
		if( !p ) par[cur] = 1;
		else {
			int q = son[p][x];
			if( len[q] == len[p] + 1 ) par[cur] = q;
			else {
				int ct = ++ cnt;
				len[ct] = len[p] + 1 , par[ct] = par[q];
				memcpy( son[ct] , son[q] , sizeof son[ct] );
				par[q] = par[cur] = ct;
				for( ; son[p][x] == q ; p = par[p] ) son[p][x] = ct;
			}
		}
		lst = cur;
		add( rt[cur] , 1 , n , i );
	}
	int merge( int u , int v , int l , int r ) {
		if( !u || !v ) return u ^ v;
		int cur = ++ cn , m = l + r >> 1;
		T[cur] = T[u] + T[v];
		if( l == r ) return cur;
		ls[cur] = merge( ls[u] , ls[v] , l , m ) , rs[cur] = merge( rs[u] , rs[v] , m + 1 , r );
		return cur;
	}
	int que( int rt , int l , int r , int L , int R ) {
		if( !rt ) return 0;
		if( L <= l && R >= r ) return 1;
		int m = l + r >> 1 , ans = 0;
		if( L <= m ) ans |= que( ls[rt] , l , m , L , R );
		if( R > m ) ans |= que( rs[rt] , m + 1 , r , L , R );
		return ans;
	}
	int yusland( int rt , int l , int r ) {
		if( l == r ) return l;
		int m = l + r >> 1;
		if( ls[rt] ) return yusland( ls[rt] , l , m );
		if( rs[rt] ) return yusland( rs[rt] , m + 1 , r );
	}
	int g[MAXN][20];
	void dfs( int u ) {
		for( int v : G[u] ) {
			g[v][0] = u;
			for( int k = 1 ; k < 19 ; ++ k ) if( g[g[v][k-1]][k-1] ) g[u][k] = g[g[v][k-1]][k-1]; else break;
			dfs( v ) , rt[u] = merge( rt[u] , rt[v] , 1 , n );
		}
	}
	int jump( int u , int l , int r ) {
		for( int k = 18 ; k >= 0 ; -- k ) {
			int v = g[u][k];
			if( !v || que( rt[v] , 1 , n , l + len[v] - 1 , r ) ) continue;
			u = g[u][k];
		}
		return par[u];
	}
	int dp[MAXN] , ans = 0;
	void sol( int u ) {
		if( u != 1 ) {
			int ps = yusland( rt[u] , 1 , n ) , v = jump( u , ps - len[u] + 1 , ps );
			dp[u] = dp[v] + 1;
		}
		ans = max( ans , dp[u] );
		for( int v : G[u] )
			sol( v );
	}
	void fuck( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) cout << i << ' ' << dp[i] << endl;
	}
} S ;

void solve() {
	cin >> n;
	scanf("%s",ch + 1);
	S.init();
	rep( i , 1 , n ) S.ins( ch[i] - 'a' , i );
	S.addall( );
	S.dfs( 1 );
	S.sol( 1 );
	cout << S.ans << endl;
}

signed main() {
//    int T;cin >> T;while( T-- ) solve();
    solve();
}


#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 800006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , m;
int A[MAXN];
char ch[MAXN];

struct SAM {
	int son[MAXN][26] , par[MAXN] , len[MAXN] , cnt , lst;
	void init( ) {
		lst = cnt = 1;
	}
	int rt[MAXN] , cn , ls[MAXN * 10] , rs[MAXN * 10] , T[MAXN * 10];
	void add( int& rt , int l , int r , int p ) {
		if( !rt ) rt = ++ cn;
		++ T[rt];
		if( l == r ) return;
		int m = l + r >> 1;
		if( p <= m ) add( ls[rt] , l , m , p );
		else add( rs[rt] , m + 1 , r , p );
	}
	vi G[MAXN];
	void addall( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) G[par[i]].pb( i );
	}
	void ins( int x , int i ) {
		int cur = ++ cnt , p = lst;
		len[cur] = len[lst] + 1;
		while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
		if( !p ) par[cur] = 1;
		else {
			int q = son[p][x];
			if( len[q] == len[p] + 1 ) par[cur] = q;
			else {
				int ct = ++ cnt;
				len[ct] = len[p] + 1 , par[ct] = par[q];
				memcpy( son[ct] , son[q] , sizeof son[ct] );
				par[q] = par[cur] = ct;
				for( ; son[p][x] == q ; p = par[p] ) son[p][x] = ct;
			}
		}
		lst = cur;
		add( rt[cur] , 1 , n , i );
	}
	int merge( int u , int v , int l , int r ) {
		if( !u || !v ) return u ^ v;
		int cur = ++ cn , m = l + r >> 1;
		T[cur] = T[u] + T[v];
		if( l == r ) return cur;
		ls[cur] = merge( ls[u] , ls[v] , l , m ) , rs[cur] = merge( rs[u] , rs[v] , m + 1 , r );
		return cur;
	}
	int que( int rt , int l , int r , int L , int R ) {
		if( !rt ) return 0;
		if( L <= l && R >= r ) return 1;
		int m = l + r >> 1 , ans = 0;
		if( L <= m ) ans |= que( ls[rt] , l , m , L , R );
		if( R > m ) ans |= que( rs[rt] , m + 1 , r , L , R );
		return ans;
	}
	int yusland( int rt , int l , int r ) {
		if( l == r ) return l;
		int m = l + r >> 1;
		if( ls[rt] ) return yusland( ls[rt] , l , m );
		if( rs[rt] ) return yusland( rs[rt] , m + 1 , r );
	}
	int g[MAXN][20];
	void dfs( int u ) {
		for( int v : G[u] ) {
			g[v][0] = u;
			for( int k = 1 ; k < 19 ; ++ k ) if( g[g[v][k-1]][k-1] ) g[v][k] = g[g[v][k-1]][k-1]; else break;
			dfs( v ) , rt[u] = merge( rt[u] , rt[v] , 1 , n );
		}
	}
	int jump( int u , int l , int r ) {
		for( int k = 18 ; k >= 0 ; -- k ) {
			int v = g[u][k];
			if( !v || que( rt[v] , 1 , n , l + len[v] - 1 , r ) ) continue;
			u = g[u][k];
		}
		return par[u];
	}
	int dp[MAXN] , ans = 0;
	void sol( int u ) {
		if( u != 1 ) {
			int ps = yusland( rt[u] , 1 , n ) , v = jump( u , ps - len[u] + 1 , ps );
			dp[u] = dp[v] + 1;
		}
		ans = max( ans , dp[u] );
		for( int v : G[u] )
			sol( v );
	}
	void fuck( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) cout << i << ' ' << dp[i] << endl;
	}
} S ;

void solve() {
	cin >> n;
	scanf("%s",ch + 1);
	S.init();
	rep( i , 1 , n ) S.ins( ch[i] - 'a' , i );
	S.addall( );
	S.dfs( 1 );
	S.sol( 1 );
	cout << S.ans << endl;
}

signed main() {
//    int T;cin >> T;while( T-- ) solve();
    solve();
}


#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 400006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , m;
int A[MAXN];
char ch[MAXN];

struct SAM {
	int son[MAXN][26] , par[MAXN] , len[MAXN] , cnt , lst;
	void init( ) {
		lst = cnt = 1;
	}
	int rt[MAXN] , cn , ls[MAXN * 10] , rs[MAXN * 10] , T[MAXN * 10];
	void add( int& rt , int l , int r , int p ) {
		if( !rt ) rt = ++ cn;
		++ T[rt];
		if( l == r ) return;
		int m = l + r >> 1;
		if( p <= m ) add( ls[rt] , l , m , p );
		else add( rs[rt] , m + 1 , r , p );
	}
	vi G[MAXN];
	void addall( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) G[par[i]].pb( i );
	}
	void ins( int x , int i ) {
		int cur = ++ cnt , p = lst;
		len[cur] = len[lst] + 1;
		while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
		if( !p ) par[cur] = 1;
		else {
			int q = son[p][x];
			if( len[q] == len[p] + 1 ) par[cur] = q;
			else {
				int ct = ++ cnt;
				len[ct] = len[p] + 1 , par[ct] = par[q];
				memcpy( son[ct] , son[q] , sizeof son[ct] );
				par[q] = par[cur] = ct;
				for( ; son[p][x] == q ; p = par[p] ) son[p][x] = ct;
			}
		}
		lst = cur;
		add( rt[cur] , 1 , n , i );
	}
	int merge( int u , int v , int l , int r ) {
		if( !u || !v ) return u ^ v;
		int cur = ++ cn , m = l + r >> 1;
		T[cur] = T[u] + T[v];
		if( l == r ) return cur;
		ls[cur] = merge( ls[u] , ls[v] , l , m ) , rs[cur] = merge( rs[u] , rs[v] , m + 1 , r );
		return cur;
	}
	int que( int rt , int l , int r , int L , int R ) {
		if( !rt ) return 0;
		if( L <= l && R >= r ) return 1;
		int m = l + r >> 1 , ans = 0;
		if( L <= m ) ans |= que( ls[rt] , l , m , L , R );
		if( R > m ) ans |= que( rs[rt] , m + 1 , r , L , R );
	}
	int yusland( int rt , int l , int r ) {
		if( l == r ) return l;
		int m = l + r >> 1;
		if( ls[rt] ) return yusland( ls[rt] , l , m );
		if( rs[rt] ) return yusland( rs[rt] , m + 1 , r );
	}
	int g[MAXN][20];
	void dfs( int u ) {
		for( int v : G[u] ) {
			g[v][0] = u;
			for( int k = 1 ; k < 19 ; ++ k ) if( g[g[v][k-1]][k-1] ) g[u][k] = g[g[v][k-1]][k-1]; else break;
			dfs( v ) , rt[u] = merge( rt[u] , rt[v] , 1 , n );
		}
	}
	int jump( int u , int l , int r ) {
		for( int k = 18 ; k >= 0 ; -- k ) {
			int v = g[u][k];
			if( !v || que( rt[v] , 1 , n , l + len[v] - 1 , r ) ) continue;
			u = g[u][k];
		}
		return par[u];
	}
	int dp[MAXN] , ans = 0;
	void sol( int u ) {
		if( u != 1 ) {
			int ps = yusland( rt[u] , 1 , n ) , v = jump( u , ps - len[u] + 1 , ps );
			dp[u] = dp[v] + 1;
		}
		ans = max( ans , dp[u] );
		for( int v : G[u] )
			sol( v );
	}
	void fuck( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) cout << i << ' ' << dp[i] << endl;
	}
} S ;

void solve() {
	cin >> n;
	scanf("%s",ch + 1);
	S.init();
	rep( i , 1 , n ) S.ins( ch[i] - 'a' , i );
	S.addall( );
	S.dfs( 1 );
	S.sol( 1 );
	cout << S.ans << endl;
}

signed main() {
//    int T;cin >> T;while( T-- ) solve();
    solve();
}


#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 400006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , m;
int A[MAXN];
char ch[MAXN];

struct SAM {
	int son[MAXN][26] , par[MAXN] , len[MAXN] , cnt , lst;
	void init( ) {
		lst = cnt = 1;
	}
	int rt[MAXN] , cn , ls[MAXN * 16] , rs[MAXN * 16] , T[MAXN * 16];
	void add( int& rt , int l , int r , int p ) {
		if( !rt ) rt = ++ cn;
		++ T[rt];
		if( l == r ) return;
		int m = l + r >> 1;
		if( p <= m ) add( ls[rt] , l , m , p );
		else add( rs[rt] , m + 1 , r , p );
	}
	vi G[MAXN];
	void addall( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) G[par[i]].pb( i );
	}
	void ins( int x , int i ) {
		int cur = ++ cnt , p = lst;
		len[cur] = len[lst] + 1;
		while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
		if( !p ) par[cur] = 1;
		else {
			int q = son[p][x];
			if( len[q] == len[p] + 1 ) par[cur] = q;
			else {
				int ct = ++ cnt;
				len[ct] = len[p] + 1 , par[ct] = par[q];
				memcpy( son[ct] , son[q] , sizeof son[ct] );
				par[q] = par[cur] = ct;
				for( ; son[p][x] == q ; p = par[p] ) son[p][x] = ct;
			}
		}
		lst = cur;
		add( rt[cur] , 1 , n , i );
	}
	int merge( int u , int v , int l , int r ) {
		if( !u || !v ) return u ^ v;
		int cur = ++ cn , m = l + r >> 1;
		T[cur] = T[u] + T[v];
		if( l == r ) return cur;
		ls[cur] = merge( ls[u] , ls[v] , l , m ) , rs[cur] = merge( rs[u] , rs[v] , m + 1 , r );
		return cur;
	}
	int que( int rt , int l , int r , int L , int R ) {
		if( !rt ) return 0;
		if( L <= l && R >= r ) return 1;
		int m = l + r >> 1 , ans = 0;
		if( L <= m ) ans |= que( ls[rt] , l , m , L , R );
		if( R > m ) ans |= que( rs[rt] , m + 1 , r , L , R );
	}
	int yusland( int rt , int l , int r ) {
		if( l == r ) return l;
		int m = l + r >> 1;
		if( ls[rt] ) return yusland( ls[rt] , l , m );
		if( rs[rt] ) return yusland( rs[rt] , m + 1 , r );
	}
	int g[MAXN][20];
	void dfs( int u ) {
		for( int v : G[u] ) {
			g[v][0] = u;
			for( int k = 1 ; k < 19 ; ++ k ) if( g[g[v][k-1]][k-1] ) g[u][k] = g[g[v][k-1]][k-1]; else break;
			dfs( v ) , rt[u] = merge( rt[u] , rt[v] , 1 , n );
		}
	}
	int jump( int u , int l , int r ) {
		for( int k = 18 ; k >= 0 ; -- k ) {
			int v = g[u][k];
			if( !v || que( rt[v] , 1 , n , l + len[v] - 1 , r ) ) continue;
			u = g[u][k];
		}
		return par[u];
	}
	int dp[MAXN] , ans = 0;
	void sol( int u ) {
		if( u != 1 ) {
			int ps = yusland( rt[u] , 1 , n ) , v = jump( u , ps - len[u] + 1 , ps );
			dp[u] = dp[v] + 1;
		}
		ans = max( ans , dp[u] );
		for( int v : G[u] )
			sol( v );
	}
	void fuck( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) cout << i << ' ' << dp[i] << endl;
	}
} S ;

void solve() {
	cin >> n;
	scanf("%s",ch + 1);
	S.init();
	rep( i , 1 , n ) S.ins( ch[i] - 'a' , i );
	S.addall( );
	S.dfs( 1 );
	S.sol( 1 );
	cout << S.ans << endl;
}

signed main() {
//    int T;cin >> T;while( T-- ) solve();
    solve();
}


#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 800006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , m;
int A[MAXN];
char ch[MAXN];

struct SAM {
	int son[MAXN][26] , par[MAXN] , len[MAXN] , cnt , lst;
	void init( ) {
		lst = cnt = 1;
	}
	int rt[MAXN] , cn , ls[MAXN * 10] , rs[MAXN * 10] , T[MAXN * 10];
	void add( int& rt , int l , int r , int p ) {
		if( !rt ) rt = ++ cn;
		++ T[rt];
		if( l == r ) return;
		int m = l + r >> 1;
		if( p <= m ) add( ls[rt] , l , m , p );
		else add( rs[rt] , m + 1 , r , p );
	}
	vi G[MAXN];
	void addall( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) G[par[i]].pb( i );
	}
	void ins( int x , int i ) {
		int cur = ++ cnt , p = lst;
		len[cur] = len[lst] + 1;
		while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
		if( !p ) par[cur] = 1;
		else {
			int q = son[p][x];
			if( len[q] == len[p] + 1 ) par[cur] = q;
			else {
				int ct = ++ cnt;
				len[ct] = len[p] + 1 , par[ct] = par[q];
				memcpy( son[ct] , son[q] , sizeof son[ct] );
				par[q] = par[cur] = ct;
				for( ; son[p][x] == q ; p = par[p] ) son[p][x] = ct;
			}
		}
		lst = cur;
		add( rt[cur] , 1 , n , i );
	}
	int merge( int u , int v , int l , int r ) {
		if( !u || !v ) return u ^ v;
		int cur = ++ cn , m = l + r >> 1;
		T[cur] = T[u] + T[v];
		if( l == r ) return cur;
		ls[cur] = merge( ls[u] , ls[v] , l , m ) , rs[cur] = merge( rs[u] , rs[v] , m + 1 , r );
		return cur;
	}
	int que( int rt , int l , int r , int L , int R ) {
		if( !rt ) return 0;
		if( L <= l && R >= r ) return 1;
		int m = l + r >> 1 , ans = 0;
		if( L <= m ) ans |= que( ls[rt] , l , m , L , R );
		if( R > m ) ans |= que( rs[rt] , m + 1 , r , L , R );
		return ans;
	}
	int yusland( int rt , int l , int r ) {
		if( l == r ) return l;
		int m = l + r >> 1;
		if( rs[rt] ) return yusland( rs[rt] , m + 1 , r );
		if( ls[rt] ) return yusland( ls[rt] , l , m );
	}
	int g[MAXN][20];
	void dfs( int u ) {
		for( int v : G[u] ) {
			g[v][0] = u;
			for( int k = 1 ; k < 19 ; ++ k ) if( g[g[v][k-1]][k-1] ) g[v][k] = g[g[v][k-1]][k-1]; else break;
			dfs( v ) , rt[u] = merge( rt[u] , rt[v] , 1 , n );
		}
	}
	int jump( int u , int l , int r ) {
		for( int k = 18 ; k >= 0 ; -- k ) {
			int v = g[u][k];
			if( !v || ( l + len[v] - 1 <= r - 1 && que( rt[v] , 1 , n , l + len[v] - 1 , r - 1 ) ) ) continue;
			u = g[u][k];
		}
		return par[u];
	}
	int dp[MAXN] , ans = 0;
	void sol( int u ) {
		if( u != 1 ) {
			int ps = yusland( rt[u] , 1 , n ) , v = jump( u , ps - len[u] + 1 , ps );
			dp[u] = dp[v] + 1;
		}
		ans = max( ans , dp[u] );
		for( int v : G[u] )
			sol( v );
	}
	void fuck( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) cout << i << ' ' << dp[i] << endl;
	}
} S ;

void solve() {
	cin >> n;
	scanf("%s",ch + 1);
	S.init();
	rep( i , 1 , n ) S.ins( ch[i] - 'a' , i );
	S.addall( );
	S.dfs( 1 );
	S.sol( 1 );
	cout << S.ans << endl;
//	S.fuck();
}

signed main() {
//    int T;cin >> T;while( T-- ) solve();
    solve();
}


#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 200006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , m;
int A[MAXN];
char ch[MAXN];

struct SAM {
	int son[MAXN][26] , par[MAXN] , len[MAXN] , cnt , lst;
	void init( ) {
		lst = cnt = 1;
	}
	int rt[MAXN] , cn , ls[MAXN] , rs[MAXN] , T[MAXN];
	void add( int& rt , int l , int r , int p ) {
		if( !rt ) rt = ++ cn;
		++ T[rt];
		if( l == r ) return;
		int m = l + r >> 1;
		if( p <= m ) add( ls[rt] , l , m , p );
		else add( rs[rt] , m + 1 , r , p );
	}
	vi G[MAXN];
	void addall( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) G[par[i]].pb( i );
	}
	void ins( int x , int i ) {
		int cur = ++ cnt , p = lst;
		len[cur] = len[lst] + 1;
		while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
		if( !p ) par[cur] = 1;
		else {
			int q = son[p][x];
			if( len[q] == len[p] + 1 ) par[cur] = q;
			else {
				int ct = ++ cnt;
				len[ct] = len[p] + 1 , par[ct] = par[q];
				memcpy( son[ct] , son[q] , sizeof son[ct] );
				par[q] = par[cur] = ct;
				for( ; son[p][x] == q ; p = par[p] ) son[p][x] = ct;
			}
		}
		lst = cur;
		add( rt[cur] , 1 , n , i );
	}
	int merge( int u , int v , int l , int r ) {
		if( !u || !v ) return u ^ v;
		int cur = ++ cn , m = l + r >> 1;
		T[cur] = T[u] + T[v];
		if( l == r ) return cur;
		ls[cur] = merge( ls[u] , ls[v] , l , m ) , rs[cur] = merge( rs[u] , rs[v] , m + 1 , r );
		return cur;
	}
	int que( int rt , int l , int r , int L , int R ) {
		if( !rt ) return 0;
		if( L <= l && R >= r ) return 1;
		int m = l + r >> 1 , ans = 0;
		if( L <= m ) ans |= que( ls[rt] , l , m , L , R );
		if( R > m ) ans |= que( rs[rt] , m + 1 , r , L , R );
	}
	int yusland( int rt , int l , int r ) {
		if( l == r ) return l;
		int m = l + r >> 1;
		if( ls[rt] ) return yusland( ls[rt] , l , m );
		if( rs[rt] ) return yusland( rs[rt] , m + 1 , r );
	}
	int g[MAXN][20];
	void dfs( int u ) {
		for( int v : G[u] ) {
			g[v][0] = u;
			for( int k = 1 ; k < 19 ; ++ k ) if( g[g[v][k-1]][k-1] ) g[u][k] = g[g[v][k-1]][k-1]; else break;
			dfs( v ) , rt[u] = merge( rt[u] , rt[v] , 1 , n );
		}
	}
	int jump( int u , int l , int r ) {
		for( int k = 18 ; k >= 0 ; -- k ) {
			int v = g[u][k];
			if( !v || que( rt[v] , 1 , n , l + len[v] - 1 , r ) ) continue;
			u = g[u][k];
		}
		return par[u];
	}
	int dp[MAXN] , ans = 0;
	void sol( int u ) {
		if( u != 1 ) {
			int ps = yusland( rt[u] , 1 , n ) , v = jump( u , ps - len[u] + 1 , ps );
			dp[u] = dp[v] + 1;
		}
		ans = max( ans , dp[u] );
		for( int v : G[u] )
			sol( v );
	}
	void fuck( ) {
		for( int i = 2 ; i <= cnt ; ++ i ) cout << i << ' ' << dp[i] << endl;
	}
} S ;

void solve() {
	cin >> n;
	scanf("%s",ch + 1);
	S.init();
	rep( i , 1 , n ) S.ins( ch[i] - 'a' , i );
	S.addall( );
	S.dfs( 1 );
	S.sol( 1 );
	cout << S.ans << endl;
}

signed main() {
//    int T;cin >> T;while( T-- ) solve();
    solve();
}


#include <bits/stdc++.h>
using namespace std;
const int maxN = 100000, maxC = 10000000;
int n;
char s[maxN + 10];
struct Node {
    int tr[26], fa, maxL, pos;
    bool is;
}a[maxN * 2 + 10];
int np = 1, ndCnt = 1;
int dfn[maxN + 10], dfsCnt;
int ed[maxN + 10];
vector<int> g[maxN * 2 + 10];
int val[maxN + 10], rt[maxN + 10];
int lc[maxC + 10], rc[maxC + 10], tot[maxC + 10];
int tNdCnt, ans;
int f[maxN * 2 + 10], bd[maxN * 2 + 10];

void Insert(int c) {
    int p = np; np = ++ndCnt;
    a[np].maxL = a[np].pos = a[p].maxL + 1; a[np].is = 1;
    while (p && !a[p].tr[c]) {
        a[p].tr[c] = np; p = a[p].fa;
    }
    if (p) {
        int q = a[p].tr[c];
        if (a[p].maxL + 1 == a[q].maxL) a[np].fa = q;
        else {
            int nq = ++ndCnt; a[nq] = a[q];
            a[nq].maxL = a[p].maxL + 1; a[q].fa = a[np].fa = nq;
            a[nq].is = 0;
            while (a[p].tr[c] == q) {
                a[p].tr[c] = nq; p = a[p].fa;
            }
        }
    } else a[np].fa = 1;
}

void Dfs(int p) {
    dfn[p] = dfsCnt + 1;
    if (a[p].is) {
        val[++dfsCnt] = a[p].maxL;
    }
    for (int i = 0; i < g[p].size(); ++i)
        Dfs(g[p][i]);
    ed[p] = dfsCnt;
}

void BuildTree(int &p, int ls, int rs) {
    p = ++tNdCnt;
    if (ls != rs) {
        int mid = (ls + rs) / 2;
        BuildTree(lc[p], ls, mid);
        BuildTree(rc[p], mid + 1, rs);
    }
}

void Add(int &p, int cmp, int k, int ls, int rs) {
    p = ++tNdCnt; lc[p] = lc[cmp]; rc[p] = rc[cmp];
    tot[p] = tot[cmp] + 1;
    if (ls != rs) {
        int mid = (ls + rs) / 2;
        if (k <= mid) Add(lc[p], lc[cmp], k, ls, mid);
        else Add(rc[p], rc[cmp], k, mid + 1, rs);
    }
}

int Query(int p, int l, int r, int ls, int rs) {
    if (l == ls && r == rs) return tot[p];
    else {
        int mid = (ls + rs) / 2;
        if (r <= mid) return Query(lc[p], l, r, ls, mid);
        else if (l > mid) return Query(rc[p], l, r, mid + 1, rs);
        else return Query(lc[p], l, mid, ls, mid) + Query(rc[p], mid + 1, r, mid + 1, rs);
    }
}

void Dp(int p) {
    for (int i = 0; i < g[p].size(); ++i) {
        int e = g[p][i];
        int l = a[e].pos - a[e].maxL + 1 + a[a[bd[p]].fa].maxL;
        int r = a[e].maxL;
        int v = Query(rt[ed[p]], l, r, 1, n) - Query(rt[dfn[p] - 1], l, r, 1, n);
        if (v >= 2 || p == 1) {
            f[e] = f[p] + 1; bd[e] = e;
        } else {
            f[e] = f[p]; bd[e] = bd[p];
        }
        ans = max(ans, f[e]);
        Dp(e);
    }
}

int main() {
    scanf("%d%s", &n, s + 1);
    for (int i = 1; i <= n; ++i) Insert(s[i] - 'a');
    for (int i = 2; i <= ndCnt; ++i)
        g[a[i].fa].push_back(i);
    Dfs(1);
    BuildTree(rt[0], 1, n);
    for (int i = 1; i <= dfsCnt; ++i)
        Add(rt[i], rt[i - 1], val[i], 1, n);
    Dp(1); printf("%d", ans);
}


#include <bits/stdc++.h>
using namespace std;
const int maxN = 100000, maxC = 10000000;
int n;
char s[maxN + 10];
struct Node {
    int tr[26], fa, maxL, pos;
    bool is;
}a[maxN * 2 + 10];
int np = 1, ndCnt = 1;
int dfn[maxN + 10], dfsCnt;
int ed[maxN + 10];
vector<int> g[maxN * 2 + 10];
int val[maxN + 10], rt[maxN + 10];
int lc[maxC + 10], rc[maxC + 10], tot[maxC + 10];
int tNdCnt, ans;
int f[maxN * 2 + 10], bd[maxN * 2 + 10];

void Insert(int c) {
    int p = np; np = ++ndCnt;
    a[np].maxL = a[np].pos = a[p].maxL + 1; a[np].is = 1;
    while (p && !a[p].tr[c]) {
        a[p].tr[c] = np; p = a[p].fa;
    }
    if (p) {
        int q = a[p].tr[c];
        if (a[p].maxL + 1 == a[q].maxL) a[np].fa = q;
        else {
            int nq = ++ndCnt; a[nq] = a[q];
            a[nq].maxL = a[p].maxL + 1; a[q].fa = a[np].fa = nq;
            a[nq].is = 0;
            while (a[p].tr[c] == q) {
                a[p].tr[c] = nq; p = a[p].fa;
            }
        }
    } else a[np].fa = 1;
}

void Dfs(int p) {
    dfn[p] = dfsCnt + 1;
    if (a[p].is) {
        val[++dfsCnt] = a[p].maxL;
    }
    for (int i = 0; i < g[p].size(); ++i)
        Dfs(g[p][i]);
    ed[p] = dfsCnt;
}

void BuildTree(int &p, int ls, int rs) {
    p = ++tNdCnt;
    if (ls != rs) {
        int mid = (ls + rs) / 2;
        BuildTree(lc[p], ls, mid);
        BuildTree(rc[p], mid + 1, rs);
    }
}

void Add(int &p, int cmp, int k, int ls, int rs) {
    p = ++tNdCnt; lc[p] = lc[cmp]; rc[p] = rc[cmp];
    tot[p] = tot[cmp] + 1;
    if (ls != rs) {
        int mid = (ls + rs) / 2;
        if (k <= mid) Add(lc[p], lc[cmp], k, ls, mid);
        else Add(rc[p], rc[cmp], k, mid + 1, rs);
    }
}

int Query(int p, int l, int r, int ls, int rs) {
    if (l == ls && r == rs) return tot[p];
    else {
        int mid = (ls + rs) / 2;
        if (r <= mid) return Query(lc[p], l, r, ls, mid);
        else if (l > mid) return Query(rc[p], l, r, mid + 1, rs);
        else return Query(lc[p], l, mid, ls, mid) + Query(rc[p], mid + 1, r, mid + 1, rs);
    }
}

void Dp(int p) {
    for (int i = 0; i < g[p].size(); ++i) {
        int e = g[p][i];
        int l = a[e].pos - a[e].maxL + 1 + a[a[bd[p]].fa].maxL;
        int r = a[e].pos;
        int v = Query(rt[ed[bd[p]]], l, r, 1, n) - Query(rt[dfn[bd[p]] - 1], l, r, 1, n);
        if (v >= 2 || p == 1) {
            f[e] = f[p] + 1; bd[e] = e;
        } else {
            f[e] = f[p]; bd[e] = bd[p];
        }
        ans = max(ans, f[e]);
        Dp(e);
    }
}

int main() {
    scanf("%d%s", &n, s + 1);
    for (int i = 1; i <= n; ++i) Insert(s[i] - 'a');
    for (int i = 2; i <= ndCnt; ++i)
        g[a[i].fa].push_back(i);
    Dfs(1);
    BuildTree(rt[0], 1, n);
    for (int i = 1; i <= dfsCnt; ++i)
        Add(rt[i], rt[i - 1], val[i], 1, n);
    Dp(1); printf("%d", ans);
}


#include <bits/stdc++.h>
using namespace std;
const int maxN = 200000, maxC = 10000000;
int n;
char s[maxN + 10];
struct Node {
    int tr[26], fa, maxL, pos;
    bool is;
}a[maxN * 2 + 10];
int np = 1, ndCnt = 1;
int dfn[maxN * 2 + 10], dfsCnt;
int ed[maxN * 2 + 10];
vector<int> g[maxN * 2 + 10];
int val[maxN + 10], rt[maxN + 10];
int lc[maxC + 10], rc[maxC + 10], tot[maxC + 10];
int tNdCnt, ans;
int f[maxN * 2 + 10], bd[maxN * 2 + 10];

void Insert(int c) {
    int p = np; np = ++ndCnt;
    a[np].maxL = a[np].pos = a[p].maxL + 1; a[np].is = 1;
    while (p && !a[p].tr[c]) {
        a[p].tr[c] = np; p = a[p].fa;
    }
    if (p) {
        int q = a[p].tr[c];
        if (a[p].maxL + 1 == a[q].maxL) a[np].fa = q;
        else {
            int nq = ++ndCnt; a[nq] = a[q];
            a[nq].maxL = a[p].maxL + 1; a[q].fa = a[np].fa = nq;
            a[nq].is = 0;
            while (a[p].tr[c] == q) {
                a[p].tr[c] = nq; p = a[p].fa;
            }
        }
    } else a[np].fa = 1;
}

void Dfs(int p) {
    dfn[p] = dfsCnt + 1;
    if (a[p].is) {
        val[++dfsCnt] = a[p].maxL;
    }
    for (int i = 0; i < g[p].size(); ++i)
        Dfs(g[p][i]);
    ed[p] = dfsCnt;
}

void BuildTree(int &p, int ls, int rs) {
    p = ++tNdCnt;
    if (ls != rs) {
        int mid = (ls + rs) / 2;
        BuildTree(lc[p], ls, mid);
        BuildTree(rc[p], mid + 1, rs);
    }
}

void Add(int &p, int cmp, int k, int ls, int rs) {
    p = ++tNdCnt; lc[p] = lc[cmp]; rc[p] = rc[cmp];
    tot[p] = tot[cmp] + 1;
    if (ls != rs) {
        int mid = (ls + rs) / 2;
        if (k <= mid) Add(lc[p], lc[cmp], k, ls, mid);
        else Add(rc[p], rc[cmp], k, mid + 1, rs);
    }
}

int Query(int p, int l, int r, int ls, int rs) {
    if (l == ls && r == rs) return tot[p];
    else {
        int mid = (ls + rs) / 2;
        if (r <= mid) return Query(lc[p], l, r, ls, mid);
        else if (l > mid) return Query(rc[p], l, r, mid + 1, rs);
        else return Query(lc[p], l, mid, ls, mid) + Query(rc[p], mid + 1, r, mid + 1, rs);
    }
}

void Dp(int p) {
    for (int i = 0; i < g[p].size(); ++i) {
        int e = g[p][i];
        int l = a[e].pos - a[e].maxL + 1 + a[a[bd[p]].fa].maxL;
        int r = a[e].pos;
        int v = Query(rt[ed[bd[p]]], l, r, 1, n) - Query(rt[dfn[bd[p]] - 1], l, r, 1, n);
        if (v >= 2 || p == 1) {
            f[e] = f[p] + 1; bd[e] = e;
        } else {
            f[e] = f[p]; bd[e] = bd[p];
        }
        ans = max(ans, f[e]);
        Dp(e);
    }
}

int main() {
    scanf("%d%s", &n, s + 1);
    for (int i = 1; i <= n; ++i) Insert(s[i] - 'a');
    for (int i = 2; i <= ndCnt; ++i)
        g[a[i].fa].push_back(i);
    Dfs(1);
    BuildTree(rt[0], 1, n);
    for (int i = 1; i <= dfsCnt; ++i)
        Add(rt[i], rt[i - 1], val[i], 1, n);
    Dp(1); printf("%d", ans);
}


#include <bits/stdc++.h>
using namespace std;
const int maxN = 200000, maxC = 10000000;
int n;
char s[maxN + 10];
struct Node {
    int tr[26], fa, maxL, pos;
    bool is;
}a[maxN * 2 + 10];
int np = 1, ndCnt = 1;
int dfn[maxN + 10], dfsCnt;
int ed[maxN + 10];
vector<int> g[maxN * 2 + 10];
int val[maxN + 10], rt[maxN + 10];
int lc[maxC + 10], rc[maxC + 10], tot[maxC + 10];
int tNdCnt, ans;
int f[maxN * 2 + 10], bd[maxN * 2 + 10];

void Insert(int c) {
    int p = np; np = ++ndCnt;
    a[np].maxL = a[np].pos = a[p].maxL + 1; a[np].is = 1;
    while (p && !a[p].tr[c]) {
        a[p].tr[c] = np; p = a[p].fa;
    }
    if (p) {
        int q = a[p].tr[c];
        if (a[p].maxL + 1 == a[q].maxL) a[np].fa = q;
        else {
            int nq = ++ndCnt; a[nq] = a[q];
            a[nq].maxL = a[p].maxL + 1; a[q].fa = a[np].fa = nq;
            a[nq].is = 0;
            while (a[p].tr[c] == q) {
                a[p].tr[c] = nq; p = a[p].fa;
            }
        }
    } else a[np].fa = 1;
}

void Dfs(int p) {
    dfn[p] = dfsCnt + 1;
    if (a[p].is) {
        val[++dfsCnt] = a[p].maxL;
    }
    for (int i = 0; i < g[p].size(); ++i)
        Dfs(g[p][i]);
    ed[p] = dfsCnt;
}

void BuildTree(int &p, int ls, int rs) {
    p = ++tNdCnt;
    if (ls != rs) {
        int mid = (ls + rs) / 2;
        BuildTree(lc[p], ls, mid);
        BuildTree(rc[p], mid + 1, rs);
    }
}

void Add(int &p, int cmp, int k, int ls, int rs) {
    p = ++tNdCnt; lc[p] = lc[cmp]; rc[p] = rc[cmp];
    tot[p] = tot[cmp] + 1;
    if (ls != rs) {
        int mid = (ls + rs) / 2;
        if (k <= mid) Add(lc[p], lc[cmp], k, ls, mid);
        else Add(rc[p], rc[cmp], k, mid + 1, rs);
    }
}

int Query(int p, int l, int r, int ls, int rs) {
    if (l == ls && r == rs) return tot[p];
    else {
        int mid = (ls + rs) / 2;
        if (r <= mid) return Query(lc[p], l, r, ls, mid);
        else if (l > mid) return Query(rc[p], l, r, mid + 1, rs);
        else return Query(lc[p], l, mid, ls, mid) + Query(rc[p], mid + 1, r, mid + 1, rs);
    }
}

void Dp(int p) {
    for (int i = 0; i < g[p].size(); ++i) {
        int e = g[p][i];
        int l = a[e].pos - a[e].maxL + 1 + a[a[bd[p]].fa].maxL;
        int r = a[e].pos;
        int v = Query(rt[ed[bd[p]]], l, r, 1, n) - Query(rt[dfn[bd[p]] - 1], l, r, 1, n);
        if (v >= 2 || p == 1) {
            f[e] = f[p] + 1; bd[e] = e;
        } else {
            f[e] = f[p]; bd[e] = bd[p];
        }
        ans = max(ans, f[e]);
        Dp(e);
    }
}

int main() {
    scanf("%d%s", &n, s + 1);
    for (int i = 1; i <= n; ++i) Insert(s[i] - 'a');
    for (int i = 2; i <= ndCnt; ++i)
        g[a[i].fa].push_back(i);
    Dfs(1);
    BuildTree(rt[0], 1, n);
    for (int i = 1; i <= dfsCnt; ++i)
        Add(rt[i], rt[i - 1], val[i], 1, n);
    Dp(1); printf("%d", ans);
}


/*
 * @Author: BilyHurington
 * @Date: 2020-08-07 16:22:03
 * @LastEditors: BilyHurington
 * @LastEditTime: 2020-08-07 21:54:39
 */
#include<bits/stdc++.h>
using namespace std;
int n;char s[200010];
namespace Segment_Tree{
    struct Node{int ls,rs;}N[10000010];
    int seg_tot;
    void Modify(int &k,int l,int r,int x){
        if(!k) k=++seg_tot;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(x<=mid) Modify(N[k].ls,l,mid,x);
        else Modify(N[k].rs,mid+1,r,x);
    }
    bool Query(int k,int l,int r,int x,int y){
        if(!k) return 0;
        if(x<=l&&r<=y) return 1;
        int mid=(l+r)>>1;
        if(x<=mid&&Query(N[k].ls,l,mid,x,y)) return 1;
        if(mid<y&&Query(N[k].rs,mid+1,r,x,y)) return 1;
        return 0;
    }
    int Merge(int rt1,int rt2){
        if(!rt1||!rt2) return rt1|rt2;
        int k=++seg_tot;
        N[k].ls=Merge(N[rt1].ls,N[rt2].ls);
        N[k].rs=Merge(N[rt1].rs,N[rt2].rs);
        return k;
    }
}
using namespace Segment_Tree;
int rt[400010],pos[400010];
namespace Suffix_Automation{
    int ch[400010][26],fail[400010],len[400010],node_tot,last_node;
    void Init(){node_tot=last_node=1;}
    void Insert(int c,int ps){
        int now=++node_tot,p=last_node;
        len[now]=len[last_node]+1;
        pos[now]=ps;
        Modify(rt[now],1,n,ps);
        last_node=now;
        for(;p&&!ch[p][c];p=fail[p]) ch[p][c]=now;
        if(!p){fail[now]=1;return;}
        int noww=ch[p][c];
        if(len[noww]==len[p]+1){fail[now]=noww;return;}
        int clone_noww=++node_tot;
        fail[clone_noww]=fail[noww];
        memcpy(ch[clone_noww],ch[noww],sizeof(ch[noww]));
        len[clone_noww]=len[p]+1;
        for(;p&&ch[p][c]==noww;p=fail[p]) ch[p][c]=clone_noww;
        fail[now]=fail[noww]=clone_noww;
    }
}
using namespace Suffix_Automation;
vector<int> T[400010];
void dfs_sam(int x){
    for(int i=0;i<T[x].size();i++){
        dfs_sam(T[x][i]);
        rt[x]=Merge(rt[x],rt[T[x][i]]);
    }
}
int f[400010],rot[400010];
void dfs_solve(int x){
    if(fail[x]==1) f[x]=1,rot[x]=x;
    else{
        if(Query(rt[rot[fail[x]]],1,n,pos[x]-len[x]+len[rot[fail[x]]],pos[x]-1)) f[x]=f[fail[x]]+1,rot[x]=x;
        else f[x]=f[fail[x]],rot[x]=rot[fail[x]];
    }
    for(int i=0;i<T[x].size();i++) dfs_solve(T[x][i]);
}
int main(){
    scanf("%d%s",&n,s+1);Init();
    for(int i=1;i<=n;i++) Insert(s[i]-'a',i);
    for(int i=2;i<=node_tot;i++) T[fail[i]].push_back(i);
    dfs_sam(1);
    for(int i=0;i<T[1].size();i++) dfs_solve(T[1][i]);
    int ans=0;
    for(int i=1;i<=node_tot;i++) ans=max(ans,f[i]);
    printf("%d",ans);
    return 0;
}


/*
 * @Author: BilyHurington
 * @Date: 2020-08-07 16:22:03
 * @LastEditors: BilyHurington
 * @LastEditTime: 2020-08-07 21:54:39
 */
#include<bits/stdc++.h>
using namespace std;
int n;char s[200010];
namespace Segment_Tree{
    struct Node{int ls,rs;}N[10000010];
    int seg_tot;
    void Modify(int &k,int l,int r,int x){
        if(!k) k=++seg_tot;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(x<=mid) Modify(N[k].ls,l,mid,x);
        else Modify(N[k].rs,mid+1,r,x);
    }
    bool Query(int k,int l,int r,int x,int y){
        if(!k) return 0;
        if(x<=l&&r<=y) return 1;
        int mid=(l+r)>>1;
        if(x<=mid&&Query(N[k].ls,l,mid,x,y)) return 1;
        if(mid<y&&Query(N[k].rs,mid+1,r,x,y)) return 1;
        return 0;
    }
    int Merge(int rt1,int rt2){
        if(!rt1||!rt2) return rt1|rt2;
        int k=++seg_tot;
        N[k].ls=Merge(N[rt1].ls,N[rt2].ls);
        N[k].rs=Merge(N[rt1].rs,N[rt2].rs);
        return k;
    }
}
using namespace Segment_Tree;
int rt[400010],pos[400010];
namespace Suffix_Automation{
    int ch[400010][26],fail[400010],len[400010],node_tot,last_node;
    void Init(){node_tot=last_node=1;}
    void Insert(int c,int ps){
        int now=++node_tot,p=last_node;
        len[now]=len[last_node]+1;
        pos[now]=ps;
        Modify(rt[now],1,n,ps);
        last_node=now;
        for(;p&&!ch[p][c];p=fail[p]) ch[p][c]=now;
        if(!p){fail[now]=1;return;}
        int noww=ch[p][c];
        if(len[noww]==len[p]+1){fail[now]=noww;return;}
        int clone_noww=++node_tot;
        pos[clone_noww]=pos[noww];
        fail[clone_noww]=fail[noww];
        memcpy(ch[clone_noww],ch[noww],sizeof(ch[noww]));
        len[clone_noww]=len[p]+1;
        for(;p&&ch[p][c]==noww;p=fail[p]) ch[p][c]=clone_noww;
        fail[now]=fail[noww]=clone_noww;
    }
}
using namespace Suffix_Automation;
vector<int> T[400010];
void dfs_sam(int x){
    for(int i=0;i<T[x].size();i++){
        dfs_sam(T[x][i]);
        rt[x]=Merge(rt[x],rt[T[x][i]]);
    }
}
int f[400010],rot[400010];
void dfs_solve(int x){
    if(fail[x]==1) f[x]=1,rot[x]=x;
    else{
        if(Query(rt[rot[fail[x]]],1,n,pos[x]-len[x]+len[rot[fail[x]]],pos[x]-1)) f[x]=f[fail[x]]+1,rot[x]=x;
        else f[x]=f[fail[x]],rot[x]=rot[fail[x]];
    }
    for(int i=0;i<T[x].size();i++) dfs_solve(T[x][i]);
}
int main(){
    scanf("%d %s",&n,s+1);Init();
    for(int i=1;i<=n;i++) Insert(s[i]-'a',i);
    for(int i=2;i<=node_tot;i++) T[fail[i]].push_back(i);
    dfs_sam(1);
    for(int i=0;i<T[1].size();i++) dfs_solve(T[1][i]);
    int ans=0;
    for(int i=1;i<=node_tot;i++) ans=max(ans,f[i]);
    printf("%d",ans);
    return 0;
}


/*
 * @Author: BilyHurington
 * @Date: 2020-08-07 16:22:03
 * @LastEditors: BilyHurington
 * @LastEditTime: 2020-08-07 21:54:39
 */
#include<bits/stdc++.h>
using namespace std;
int n;char s[200010];
namespace Segment_Tree{
    struct Node{int ls,rs;}N[10000010];
    int seg_tot;
    void Modify(int &k,int l,int r,int x){
        if(!k) k=++seg_tot;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(x<=mid) Modify(N[k].ls,l,mid,x);
        else Modify(N[k].rs,mid+1,r,x);
    }
    bool Query(int k,int l,int r,int x,int y){
        if(!k) return 0;
        if(x<=l&&r<=y) return 1;
        int mid=(l+r)>>1;
        if(x<=mid&&Query(N[k].ls,l,mid,x,y)) return 1;
        if(mid<y&&Query(N[k].rs,mid+1,r,x,y)) return 1;
        return 0;
    }
    int Merge(int rt1,int rt2){
        if(!rt1||!rt2) return rt1|rt2;
        int k=++seg_tot;
        N[k].ls=Merge(N[rt1].ls,N[rt2].ls);
        N[k].rs=Merge(N[rt1].rs,N[rt2].rs);
        return k;
    }
}
using namespace Segment_Tree;
int rt[400010],pos[400010];
namespace Suffix_Automation{
    int ch[400010][26],fail[400010],len[400010],node_tot,last_node;
    void Init(){node_tot=last_node=1;}
    void Insert(int c,int ps){
        int now=++node_tot,p=last_node;
        len[now]=len[last_node]+1;
        pos[now]=ps;
        Modify(rt[now],1,n,ps);
        last_node=now;
        for(;p&&!ch[p][c];p=fail[p]) ch[p][c]=now;
        if(!p){fail[now]=1;return;}
        int noww=ch[p][c];
        if(len[noww]==len[p]+1){fail[now]=noww;return;}
        int clone_noww=++node_tot;
        fail[clone_noww]=fail[noww];
        memcpy(ch[clone_noww],ch[noww],sizeof(ch[noww]));
        len[clone_noww]=len[p]+1;
        for(;p&&ch[p][c]==noww;p=fail[p]) ch[p][c]=clone_noww;
        fail[now]=fail[noww]=clone_noww;
    }
}
using namespace Suffix_Automation;
vector<int> T[400010];
void dfs_sam(int x){
    for(int i=0;i<T[x].size();i++){
        dfs_sam(T[x][i]);
        rt[x]=Merge(rt[x],rt[T[x][i]]);
    }
}
int f[400010],rot[400010];
void dfs_solve(int x){
    if(fail[x]==1) f[x]=1,rot[x]=x;
    else{
        if(Query(rt[rot[fail[x]]],1,n,pos[x]-len[x]+len[rot[fail[x]]],pos[x]-1)) f[x]=f[fail[x]]+1,rot[x]=x;
        else f[x]=f[fail[x]],rot[x]=rot[fail[x]];
    }
    for(int i=0;i<T[x].size();i++) dfs_solve(T[x][i]);
}
int main(){
    scanf("%d%s",&n,s+1);
    Init();
    for(int i=1;i<=n;i++) Insert(s[i]-'a',i);
    for(int i=2;i<=node_tot;i++) T[fail[i]].push_back(i);
    dfs_sam(1);
    for(int i=0;i<T[1].size();i++) dfs_solve(T[1][i]);
    int ans=0;
    for(int i=1;i<=node_tot;i++) ans=max(ans,f[i]);
    printf("%d",ans);
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
int n;char s[200010];
namespace Segment_Tree{
    struct Node{int ls,rs;}N[10000010];
    int seg_tot;
    void Modify(int &k,int l,int r,int x){
        if(!k) k=++seg_tot;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(x<=mid) Modify(N[k].ls,l,mid,x);
        else Modify(N[k].rs,mid+1,r,x);
    }
    bool Query(int k,int l,int r,int x,int y){
        if(!k) return 0;
        if(x<=l&&r<=y) return 1;
        int mid=(l+r)>>1;
        if(x<=mid&&Query(N[k].ls,l,mid,x,y)) return 1;
        if(mid<y&&Query(N[k].rs,mid+1,r,x,y)) return 1;
        return 0;
    }
    int Merge(int rt1,int rt2){
        if(!rt1||!rt2) return rt1|rt2;
        int k=++seg_tot;
        N[k].ls=Merge(N[rt1].ls,N[rt2].ls);
        N[k].rs=Merge(N[rt1].rs,N[rt2].rs);
        return k;
    }
}
using namespace Segment_Tree;
int rt[400010],pos[400010];
namespace Suffix_Automation{
    int ch[400010][26],fail[400010],len[400010],node_tot,last_node;
    void Init(){node_tot=last_node=1;}
    void Insert(int c,int ps){
        int now=++node_tot,p=last_node;
        len[now]=len[last_node]+1;
        pos[now]=ps;
        Modify(rt[now],1,n,ps);
        last_node=now;
        for(;p&&!ch[p][c];p=fail[p]) ch[p][c]=now;
        if(!p){fail[now]=1;return;}
        int noww=ch[p][c];
        if(len[noww]==len[p]+1){fail[now]=noww;return;}
        int clone_noww=++node_tot;
        pos[clone_noww]=pos[noww];
        fail[clone_noww]=fail[noww];
        memcpy(ch[clone_noww],ch[noww],sizeof(ch[noww]));
        len[clone_noww]=len[p]+1;
        for(;p&&ch[p][c]==noww;p=fail[p]) ch[p][c]=clone_noww;
        fail[now]=fail[noww]=clone_noww;
    }
}
using namespace Suffix_Automation;
vector<int> T[400010];
void dfs_sam(int x){
    for(int i=0;i<T[x].size();i++){
        dfs_sam(T[x][i]);
        rt[x]=Merge(rt[x],rt[T[x][i]]);
    }
}
int f[400010],rot[400010];
void dfs_solve(int x){
    if(fail[x]==1) f[x]=1,rot[x]=x;
    else{
        if(Query(rt[rot[fail[x]]],1,n,pos[x]-len[x]+len[rot[fail[x]]],pos[x]-1)) f[x]=f[fail[x]]+1,rot[x]=x;
        else f[x]=f[fail[x]],rot[x]=rot[fail[x]];
    }
    for(int i=0;i<T[x].size();i++) dfs_solve(T[x][i]);
}
int main(){
    scanf("%d %s",&n,s+1);Init();
    for(int i=1;i<=n;i++) Insert(s[i]-'a',i);
    for(int i=2;i<=node_tot;i++) T[fail[i]].push_back(i);
    dfs_sam(1);
    for(int i=0;i<T[1].size();i++) dfs_solve(T[1][i]);
    int ans=0;
    for(int i=1;i<=node_tot;i++) ans=max(ans,f[i]);
    printf("%d",ans);
    return 0;
}


/*
 * @Author: BilyHurington
 * @Date: 2020-08-07 16:22:03
 * @LastEditors: BilyHurington
 * @LastEditTime: 2020-08-07 21:54:39
 */
#include<bits/stdc++.h>
using namespace std;
int n;char s[200010];
namespace Segment_Tree{
    struct Node{int ls,rs;}N[10000010];
    int seg_tot;
    void Modify(int &k,int l,int r,int x){
        if(!k) k=++seg_tot;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(x<=mid) Modify(N[k].ls,l,mid,x);
        else Modify(N[k].rs,mid+1,r,x);
    }
    bool Query(int k,int l,int r,int x,int y){
        if(!k) return 0;
        if(x<=l&&r<=y) return 1;
        int mid=(l+r)>>1;
        if(x<=mid&&Query(N[k].ls,l,mid,x,y)) return 1;
        if(mid<y&&Query(N[k].rs,mid+1,r,x,y)) return 1;
        return 0;
    }
    int Merge(int rt1,int rt2){
        if(!rt1||!rt2) return rt1|rt2;
        int k=++seg_tot;
        N[k].ls=Merge(N[rt1].ls,N[rt2].ls);
        N[k].rs=Merge(N[rt1].rs,N[rt2].rs);
        return k;
    }
}
using namespace Segment_Tree;
int rt[400010],pos[400010];
namespace Suffix_Automation{
    int ch[400010][26],fail[400010],len[400010],node_tot,last_node;
    void Init(){node_tot=last_node=1;}
    void Insert(int c,int ps){
        int now=++node_tot,p=last_node;
        len[now]=len[last_node]+1;
        pos[now]=ps;
        Modify(rt[now],1,n,ps);
        last_node=now;
        for(;p&&!ch[p][c];p=fail[p]) ch[p][c]=now;
        if(!p){fail[now]=1;return;}
        int noww=ch[p][c];
        if(len[noww]==len[p]+1){fail[now]=noww;return;}
        int clone_noww=++node_tot;
        pos[clone_noww]=pos[noww];
        fail[clone_noww]=fail[noww];
        memcpy(ch[clone_noww],ch[noww],sizeof(ch[noww]));
        len[clone_noww]=len[p]+1;
        for(;p&&ch[p][c]==noww;p=fail[p]) ch[p][c]=clone_noww;
        fail[now]=fail[noww]=clone_noww;
    }
}
using namespace Suffix_Automation;
vector<int> T[400010];
void dfs_sam(int x){
    for(int i=0;i<T[x].size();i++){
        dfs_sam(T[x][i]);
        rt[x]=Merge(rt[x],rt[T[x][i]]);
    }
}
int f[400010],rot[400010];
void dfs_solve(int x){
    if(fail[x]==1) f[x]=1,rot[x]=x;
    else{
        if(Query(rt[rot[fail[x]]],1,n,pos[x]-len[x]+len[rot[fail[x]]],pos[x]-1)) f[x]=f[fail[x]]+1,rot[x]=x;
        else f[x]=f[fail[x]],rot[x]=rot[fail[x]];
    }
    for(int i=0;i<T[x].size();i++) dfs_solve(T[x][i]);
}
int main(){
    scanf("%d%s",&n,s+1);Init();
    for(int i=1;i<=n;i++) Insert(s[i]-'a',i);
    for(int i=2;i<=node_tot;i++) T[fail[i]].push_back(i);
    dfs_sam(1);
    for(int i=0;i<T[1].size();i++) dfs_solve(T[1][i]);
    int ans=0;
    for(int i=1;i<=node_tot;i++) ans=max(ans,f[i]);
    printf("%d",ans);
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
#define vi vector<int>
#define pii pair<int,int>
#define vp  vector<pii>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ls l,mid,rt<<1
#define rs mid+1,r,rt<<1|1
#define de(x) cout<< #x<<" = "<<x<<endl
#define dd(x) cout<< #x<<" = "<<x<<" "
#define sf scanf
#define pf printf
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 INT128;
//INT128 read(){
//    INT128 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;
//}
//void write(INT128 x){
//    if(x<0){putchar('-');x=-x;}
//    if(x<10){putchar(x+'0'); return;}
//    write(x/10);putchar(x%10+'0');
//}
typedef long double ld;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
const double PI=acos(-1.0);
const int MAXN=5e5+5;
const int MAXM=2e6+5;
const int MOD=1e9+7;
int sgn(double x){if(fabs(x)<eps)return 0;if(x<0)return -1;else return 1;}
ll pw(ll a,ll n,ll mod){ll ret=1;while(n){if(n&1)ret=ret*a%mod;a=a*a%mod;n>>=1;}return ret;}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
void inc(ll &a,ll b){a+=b;if(a>=MOD)a-=MOD;}
void dec(ll &a,ll b){a-=b;if(a<0)a+=MOD;}
int mul(int a, int b){ll c=1ll*a*b;return c-c/MOD*MOD;}
//std::ios::sync_with_stdio(false);
//head

struct SAM{
    int fail[MAXN],nxt[MAXN][26];
    int len[MAXN],right[MAXN],endpos[MAXN];
    int sz;
    int new_node(int v,int _endpos){
        ++sz;
        fail[sz]=0,right[sz]=0,len[sz]=v,endpos[sz]=_endpos;
        rep(i,0,26)nxt[sz][i]=0;
        return sz;
    }
    int root,last;
    SAM(){clear();}
    void clear(){root=last=new_node(sz=0,0);}
    void add(int c,int _endpos){
        int p=last,np=new_node(len[p]+1,_endpos);
        while(p&&!nxt[p][c])nxt[p][c]=np,p=fail[p];
        if(!p)fail[np]=root;
        else{
            int q=nxt[p][c];
            if(len[p]+1==len[q])fail[np]=q;
            else{
                int nq=new_node(len[p]+1,_endpos);
                rep(i,0,26)nxt[nq][i]=nxt[q][i];
                fail[nq]=fail[q],fail[q]=fail[np]=nq;
                while(p&&nxt[p][c]==q)nxt[p][c]=nq,p=fail[p];
            }
        }
        last=np,right[np]=1;
    }
    int d[MAXN],b[MAXN];
    void topo(){
        rep(i,0,sz+1)d[i]=0;
        int mx=0;
        rep(i,1,sz+1)mx=max(mx,len[i]),d[len[i]]++;
        rep(i,1,mx+1)d[i]+=d[i-1];
        rep(i,1,sz+1)b[d[len[i]]--]=i;
    }
    void upd(){
        topo();
        per(i,2,sz+1)right[fail[b[i]]]+=right[b[i]];
    }
}sm;

//Seg-Tree
struct Node{int l,r;}T[MAXN*20];
int cnt,root[MAXN];
void ins(int &x,int l,int r,int pos){
    if(!x)x=++cnt;
    if(l==r)return;
    int mid=l+r>>1;
    if(pos<=mid)ins(T[x].l,l,mid,pos);
    else ins(T[x].r,mid+1,r,pos);
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    int z=++cnt;
    T[z].l=merge(T[x].l,T[y].l);
    T[z].r=merge(T[x].r,T[y].r);
    return z;
}
int qry(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,ret=0;
    if(L<=mid)ret|=qry(T[x].l,l,mid,L,min(R,mid));
    if(R>mid)ret|=qry(T[x].r,mid+1,r,max(L,mid+1),R);
    return ret;
}

//Main
int n,dp[MAXN],pos[MAXN]; char s[MAXN];
void work(){
    int sz=sm.sz,*fail=sm.fail,*b=sm.b,*endpos=sm.endpos,*len=sm.len;
    cnt=0; memset(T,0,sizeof(T));
    per(i,2,sz+1){
        int x=b[i],y=fail[x];
        ins(root[x],1,n,endpos[x]);
        root[y]=merge(root[y],root[x]);
    }
    int ans=1;
    rep(i,2,sz+1){
        int x=b[i],y=fail[x];
        if(y==1)dp[x]=1,pos[x]=x;
        else{
            if(qry(root[pos[y]],1,n,endpos[x]-len[x]+len[pos[y]],endpos[x]-1))
                dp[x]=dp[y]+1,pos[x]=x;
            else dp[x]=dp[y],pos[x]=pos[y];
        }
        ans=max(ans,dp[x]);
    }
    pf("%d\n",ans);
}
int main(){
    sf("%d%s",&n,s+1);
    rep(i,1,n+1)sm.add(s[i]-'a',i);
    sm.upd(); work();
}


//#pragma GCC diagnostic error "-std=c++11"
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include<bits/stdc++.h>
#define mod 998244353
#define ll long long
using namespace std;

int n;
char s[202000];

namespace sam{
  int ch[404000][26], fa[404000], dep[404000], cnt=1, las=1;
  int Fa[404000][20], wei[404000], st[404000];
  int dft, dfl[404000], dfr[404000], a[404000];
  vector<int>G[404000];

  void add(int x,int who){
    int np=++cnt, p=las; st[np]=wei[np]=who;
    dep[np]=dep[las]+1; las=np;
    for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
    if (!p){
      fa[np]=1; return;
    }
    int q=ch[p][x];
    if (dep[q]==dep[p]+1){
      fa[np]=q; return;
    }
    int nq=++cnt;
    dep[nq]=dep[p]+1; memcpy(ch[nq],ch[q],sizeof ch[q]); fa[nq]=fa[q];
    for (;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
    fa[q]=fa[np]=nq;
  }

  void dfs(int x){
    Fa[x][0]=fa[x];
    for (int i=1;i<=19;++i)
      Fa[x][i]=Fa[Fa[x][i-1]][i-1];
    dfl[x]=dft+1; if (wei[x]) a[++dft]=wei[x];
    for (auto y:G[x]){
      dfs(y); st[x]=st[y];
    }
    dfr[x]=dft;
  }

  int to[404000], ans[404000];
  struct QUE{
    int who, l, r, fl;
  };
  vector<QUE>que[404000];

  namespace bit{
    int c[404000], sz;
    void reset(int sz_){
      memset(c,0,sizeof c); sz=sz_;
    }
    void add(register int x){
      for (;x<=sz;x+=x&-x) ++c[x];
    }
    int que(register int x){
      int t=0;
      for (;x;x-=x&-x) t+=c[x];
      return t;
    }
    int cal(int l,int r){
      return que(r)-que(l-1);
    }
  }

  void gao(){
    bit::reset(n);
    for (int i=1;i<=dft;++i){
      bit::add(a[i]);
      for (auto o:que[i])
	ans[o.who]+=o.fl*bit::cal(o.l,o.r);
    }
  }

  void build(){
    for (int i=1;i<=cnt;++i)
      if (fa[i]) G[fa[i]].push_back(i);
    dfs(1);
    //for (int i=1;i<=cnt;++i)
    //  printf("%d : %d %d  %d,%d\n",i,dep[i],st[i],dfl[i],dfr[i]);

    for (int i=1;i<=cnt;++i) to[i]=i;
    for (int b=19;b>=0;--b){
      memset(ans,0,sizeof ans);
      for (int i=0;i<=dft;++i) que[i].clear();
      for (int i=1;i<=cnt;++i){
	int aim=Fa[to[i]][b];
	if (aim<=1){
	  ans[i]=2; continue;
	}
	int l=st[i]-dep[i]+dep[aim], r=st[i];
	//printf(" %d %d %d %d\n",dfl[aim],dfr[aim],l,r);
	que[dfl[aim]-1].push_back((QUE){i,l,r,-1});
	que[dfr[aim]].push_back((QUE){i,l,r,1});
      }
      gao();
      for (int i=1;i<=cnt;++i){
	if (ans[i]<=1) to[i]=Fa[to[i]][b];
      }
    }
  }

  int f[404000];
  void sfd(int x){
    f[x]=max(f[fa[x]],1);
    f[x]=max(f[x],f[fa[to[x]]]+1);
    if (x==1) f[x]=0;
    //printf("%d : %d\n",x,to[x]);
    for (auto y:G[x]) sfd(y);
  }
  int getdp(){
    sfd(1);
    return *max_element(f+1,f+cnt+1);
  }
}

int main(){
    cin>>n;
  cin>>s+1;// n=strlen(s+1);
  //n=2e5; for (int i=1;i<=n;++i) s[i]=min(rand()%233,1)+'a';
  for (int i=1;i<=n;++i) sam::add(s[i]-'a',i);
  sam::build();
  cout<<sam::getdp()<<endl;
}


#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 f[N][19],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)
		{
			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)
	{
		f[now][0]=t[now].fa;
		for(int i=1;i<=18;i++) f[now][i]=f[f[now][i-1]][i-1];
		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);
		}
	}

	int check(int fa,int now)
	{
//		printf("%d %d %d %d %d\n",now,fa,t[now].at-t[now].len+t[fa].len+1,t[now].at,query(rt[fa],1,n,t[now].at-t[now].len+t[fa].len,t[now].at));
		if(query(rt[fa],1,n,t[now].at-t[now].len+t[fa].len,t[now].at)>=2) return 0;
		else return 1;
	}

	int dfs1(int now)
	{
		if(now!=1)
		{
			int fa=now;
			for(int i=18;i>=0;i--)
			{
				if(!f[fa][i]) continue;
				if(check(f[fa][i],now))
				{
					fa=f[fa][i];
				}
			}
			fa=f[fa][0];
			dp[now]=dp[fa]+1;
		}
		for(int i=0;i<g[now].size();i++)
		{
			dfs1(g[now][i]);
		}
	}

	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);
		dfs1(1);
		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>
#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)
		{
//			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 dfs1(int now)
	{
		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];
		}
		for(int i=0;i<g[now].size();i++)
		{
			dfs1(g[now][i]);
		}
	}

	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=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();
}


#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<<1];

	int last=1,cnt=1,gg=0;
	int tot=0;
	int rt[N];
	int f[N][19],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)
		{
			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)
	{
		f[now][0]=t[now].fa;
		for(int i=1;i<=18;i++) f[now][i]=f[f[now][i-1]][i-1];
		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);
		}
	}

	int check(int fa,int now)
	{
//		printf("%d %d %d %d %d\n",now,fa,t[now].at-t[now].len+t[fa].len+1,t[now].at,query(rt[fa],1,n,t[now].at-t[now].len+t[fa].len,t[now].at));
		if(query(rt[fa],1,n,t[now].at-t[now].len+t[fa].len,t[now].at)>=2) return 0;
		else return 1;
	}

	int dfs1(int now)
	{
		if(now!=1)
		{
			int fa=now;
			for(int i=18;i>=0;i--)
			{
				if(!f[fa][i]) continue;
				if(check(f[fa][i],now))
				{
					fa=f[fa][i];
				}
			}
			fa=f[fa][0];
			dp[now]=dp[fa]+1;
		}
		for(int i=0;i<g[now].size();i++)
		{
			dfs1(g[now][i]);
		}
	}

	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);
		dfs1(1);
		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>
#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)
    {
        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)
    {
        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];
        }
        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();
}


#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]);
            merge(rt[now],rt[g[now][i]],1,n);
        }
    }

    int dfs1(int now)
    {
        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);
        }
        for(int i=1;i<=cnt;i++)
        {
            rt[i]=i;
            tot++;
        }
        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);
    // }
}