#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using uint = unsigned int;
#define rep(i,n) for(int i=0;i<int(n);i++)
#define rep1(i,n) for(int i=1;i<=int(n);i++)
#define per(i,n) for(int i=int(n)-1;i>=0;i--)
#define per1(i,n) for(int i=int(n);i>0;i--)
#define all(c) c.begin(),c.end()
#define si(x) int(x.size())
#define pb emplace_back
#define fs first
#define sc second
template<class T> using V = vector<T>;
template<class T> using VV = vector<vector<T>>;
template<class T,class U> void chmax(T& x, U y){if(x<y) x=y;}
template<class T,class U> void chmin(T& x, U y){if(y<x) x=y;}
template<class T> void mkuni(V<T>& v){sort(all(v));v.erase(unique(all(v)),v.end());}
template<class S,class T> ostream& operator<<(ostream& o,const pair<S,T> &p){
	return o<<"("<<p.fs<<","<<p.sc<<")";
}
template<class T> ostream& operator<<(ostream& o,const vector<T> &vc){
	o<<"{";
	for(const T& v:vc) o<<v<<",";
	o<<"}";
	return o;
}
constexpr ll TEN(int n) { return (n == 0) ? 1 : 10 * TEN(n-1); }

#ifdef LOCAL
#define show(x) cerr << "LINE" << __LINE__ << " : " << #x << " = " << (x) << endl
void dmpr(ostream& os){os<<endl;}
template<class T,class... Args>
void dmpr(ostream&os,const T&t,const Args&... args){
	os<<t<<" ~ ";
	dmpr(os,args...);
}
#define shows(...) cerr << "LINE" << __LINE__ << " : ";dmpr(cerr,##__VA_ARGS__)
#define dump(x) cerr << "LINE" << __LINE__ << " : " << #x << " = {";  \
	for(auto v: x) cerr << v << ","; cerr << "}" << endl;
#else
#define show(x) void(0)
#define dump(x) void(0)
#define shows(...) void(0)
#endif


struct SuffixArray{
	V<int> sa;
	V<int> isa;
	V<int> lcp;

	template<class T>
	SuffixArray(const vector<T>& s){	//int,ll
		int N = s.size();
		T s_arr[N];
		rep(i,N) s_arr[i] = s[i];
		int sa_arr[N+1];
		int lcp_arr[N];
		{	//zaatsu
			V<T> vs = s;
			sort(all(vs));
			vs.erase(unique(all(vs)),vs.end());
			rep(i,N) s_arr[i] = lower_bound(all(vs),s[i]) - vs.begin();
		}
		int K = N;
		SA(N,s_arr,sa_arr,K);
		LCP(N,s_arr,sa_arr,lcp_arr);
		sa = V<int>(sa_arr,sa_arr+(N+1));
		isa.resize(N+1);
		rep(i,N+1) isa[sa[i]] = i;
		lcp = V<int>(lcp_arr,lcp_arr+N);
	}
	SuffixArray(const string& s){
		int N = s.size();
		char s_arr[N];
		rep(i,N) s_arr[i] = s[i];
		int sa_arr[N+1];
		int lcp_arr[N];
		SA(N,s_arr,sa_arr,256);
		LCP(N,s_arr,sa_arr,lcp_arr);
		sa = V<int>(sa_arr,sa_arr+(N+1));
		isa.resize(N+1);
		rep(i,N+1) isa[sa[i]] = i;
		lcp = V<int>(lcp_arr,lcp_arr+N);
	}

	private:

	template<class T>
	void induce(int N,const T s[],bool is[],int sa[],int lbase[],int K){
		int it[K+1];
		copy_n(lbase,K+1,it);
		rep(i,N+1){
			if(sa[i]>=1&&!is[sa[i]-1]){
				int c=s[sa[i]-1];
				sa[it[c]++]=sa[i]-1;
			}
		}
		copy_n(lbase,K+1,it);
		for(int i=N;i>0;i--){
			if(sa[i]>=1&&is[sa[i]-1]){
				int c=s[sa[i]-1];
				sa[--it[c+1]]=sa[i]-1;
			}
		}
	}
	template<class T>
	void SA(int N,const T s[],int sa[],int K){
		bool is[N+1];		//stype?
		int lcnt[K+1]={},scnt[K+1]={};
		is[N]=1;
		for(int i=N-1;i>=0;i--){
			if(i==N-1||s[i]>s[i+1]) is[i]=0;
			else if(s[i]<s[i+1]) is[i]=1;
			else is[i]=is[i+1];
			if(!is[i]) lcnt[(int)s[i]]++;
			else scnt[(int)s[i]]++;
		}
		vector<int> v;		//LMSs
		int lms[N+1];
		fill_n(lms,N+1,-1);
		int c=0;
		rep1(i,N-1){
			if(!is[i-1]&&is[i]){
				lms[i]=c++;
				v.pb(i);
			}
		}
		int lbase[K+1],sbase[K+1];
		lbase[0]=1,sbase[0]=1+lcnt[0];
		rep1(i,K){
			lbase[i]=sbase[i-1]+scnt[i-1];
			sbase[i]=lbase[i]+lcnt[i];
		}
		if(!v.empty()){
			vector<int> v2=v;
			int it[K+1];			//iterate
			copy_n(sbase,K+1,it);
			fill_n(sa,N+1,-1);
			sa[0]=N;
			rep(i,v.size()){
				int c=s[v[i]];
				sa[it[c]++]=v[i];
			}
			induce(N,s,is,sa,lbase,K);
			int c=0;
			rep1(i,N) if(lms[sa[i]]>=0) v[c++]=sa[i];
			int s2[v.size()],sa2[v.size()+1];
			c=0;
			s2[lms[v[0]]]=0;
			for(int i=1;i<(int)v.size();i++){
				int l=v[i-1],r=v[i];
				while(true){
					if(s[l]!=s[r]){
						c++;
						break;
					}
					l++,r++;
					if(lms[l]>=0||lms[r]>=0){
						if(lms[l]<0||lms[r]<0) c++;
						break;
					}
				}
				s2[lms[v[i]]]=c;
			}
			SA(v.size(),s2,sa2,c);
			rep1(i,v.size()) v[i-1]=v2[sa2[i]];
		}
		int it[K+1];
		copy_n(sbase,K+1,it);
		fill_n(sa,N+1,-1);
		sa[0]=N;
		rep(i,v.size()){
			int c=s[v[i]];
			sa[it[c]++]=v[i];
		}
		induce(N,s,is,sa,lbase,K);
	}
	template<class T>
	void LCP(int N,const T s[],const int sa[],int lcp[]){
		int isa[N+1];
		rep(i,N+1) isa[sa[i]]=i;
		int h=0;
		rep(i,N){
			int j=sa[isa[i]-1];
			if(h>0) h--;
			for(;j+h<N&&i+h<N;h++){
				if(s[j+h]!=s[i+h]) break;
			}
			lcp[isa[i]-1]=h;
		}
	}
};
int bsr(unsigned int x){
	if(x==0) return -1;
	return 31 ^ __builtin_clz(x);
}

struct SparseTable{
	using D = int;												//type
	VV<D> d;
	int lg;

	SparseTable(){}
	SparseTable(const vector<D>& v){
		int N = v.size();
		assert(N > 0);

		lg = bsr(N)+1;
		d.resize(lg);
		d[0] = v;
		for(int t=1;t<lg;t++){
			int len = 1<<t;
			d[t].resize(N-len+1);
			rep(i,N-len+1){
				d[t][i] = min(d[t-1][i] , d[t-1][i+len/2]);		//op
			}
		}
	}
	D query(int l, int r) const {
		if(l>=r) return 1e9;										//e
		int u = bsr(r-l);
		return min(d[u][l] , d[u][r-(1<<u)]);					//op
	}
};


int main(){
	cin.tie(0);
	ios::sync_with_stdio(false);		//DON'T USE scanf/printf/puts !!
	cout << fixed << setprecision(20);

	int N;string s; cin >> N >> s;
	SuffixArray SA(s);
	int ans = 0;
	const auto& isa = SA.isa;
	show(SA.lcp);
	SparseTable ST(SA.lcp);
	rep(i,N) rep(j,i){
		int x = isa[i], y = isa[j];
		if(x>y) swap(x,y);
		int w = min(ST.query(x,y),i-j);
		chmax(ans,w);
	}
	cout << ans << endl;
}


Measured memory_bytes: 640