//#pragma GCC optimize ("-O3","unroll-loops")
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<math.h>
#include<iomanip>
#include<set>
#include<numeric>
#include<cstring>
#include<cstdio>
#include<functional>
#include<bitset>
#include<limits.h>
#include<cassert>
#include<iterator>
#include<complex>
#include<stack>


#define REP(i, n) for(int i = 0;i < n;i++)
#define REPR(i, n) for(int i = n;i >= 0;i--)
#define FOR(i, m, n) for(int i = m;i < n;i++)
#define FORR(i, m, n) for(int i = m;i >= n;i--)
#define SORT(v, n) sort(v, v+n);
#define VSORT(v) sort(v.begin(), v.end());
#define REVERSE(v,n) reverse(v,v+n);
#define VREVERSE(v) reverse(v.begin(), v.end());
#define ll long long
#define pb(a) push_back(a)
#define m0(x) memset(x,0,sizeof(x))
#define print(x) cout<<x<<'\n';
#define pe(x) cout<<x<<" ";
#define lb(v,n) lower_bound(v.begin(), v.end(), n);
#define ub(v,n) upper_bound(v.begin(), v.end(), n);
//#define int long long
#define all(x) (x).begin(), (x).end()
//#define double long double

using namespace std;

template<class T> inline bool chmin(T& a, T b) {
	if (a > b) {
		a = b;
		return true;
	}
	return false;
}
template<class T> inline bool chmax(T& a, T b) {
	if (a < b) {
		a = b;
		return true;
	}
	return false;
}

//const int MOD = 1e9 + 7;
const ll INF = 1e17;
const int INT_INF = 1e9;
const double pi = acos(-1);
const double EPS = 1e-10;
typedef pair<int, int>P;
const int MAX = 200020;

template<typename T,T MOD1,T MOD2,T B1,T B2>
struct RollingHash {
	vector<T>hash[2], p[2];
	T MOD[2] = { MOD1,MOD2 };
	T B[2] = { B1,B2 };
	RollingHash(const string &s) {
		int N = s.size();
		for (int k = 0; k < 2;k++) {
			hash[k].assign(N + 1, 0);
			p[k].assign(N + 1, 1);
			for (int i = 0; i<N; i++) {
				hash[k][i + 1] = (hash[k][i] * B[k] + s[i]) % MOD[k];
				p[k][i + 1] = p[k][i] * B[k] % MOD[k];
			}
		}
	}

	//S[l,r)
	pair<T, T>find(int l, int r) {
		T res1 = hash[0][r] + MOD[0] - hash[0][l] * p[0][r - l] % MOD[0];
		res1 = (res1 >= MOD[0]) ? res1 - MOD[0] : res1;
		T res2 = hash[1][r] + MOD[1] - hash[1][l] * p[1][r - l] % MOD[1];
		res2 = (res2 >= MOD[1]) ? res2 - MOD[1] : res2;

		return make_pair(res1, res2);
	}
	T find1(int l, int r) {
		T res1 = hash[0][r] + MOD[0] - hash[0][l] * p[0][r - l] % MOD[0];
		return (res1 >= MOD[0]) ? res1 - MOD[0] : res1;
	}
	T find2(int l, int r) {
		T res2 = hash[1][r] + MOD[1] - hash[1][l] * p[1][r - l] % MOD[1];
		return (res2 >= MOD[1]) ? res2 - MOD[1] : res2;
	}
};
//using RH = RollingHash<ll, (ll)1e9 + 7, (ll)1e9 + 9, 1234567, 7654343>;


signed main() {
	cin.tie(0);
	ios::sync_with_stdio(false);

	int N; string S; cin >> N >> S;
	using RH = RollingHash<ll, (ll)1e9+7,(ll)1e9+9,12567,76343>;
	RH rh(S);
	ll ok = 0, ng = N / 2 + 1;
	while (ng - ok > 1) {
		int mid = (ok + ng) / 2;
		bool solve = false;
		REP(i, N - 2 * mid + 1) {
			FOR(j, i + mid, N - mid + 1) {
				/*if (rh.find(i, i + mid) == rh.find(j, j + mid)) {
					solve = true;
					goto END;
				}*/
				if (rh.find1(i, i + mid) == rh.find1(j, j + mid) && rh.find2(i, i + mid) == rh.find2(j, j + mid)) {
					solve = true;
					goto END;
				}
			}
		}
	END:;
		if (solve)ok = mid; else ng = mid;
	}
	print(ok);
}

Measured memory_bytes: 480