#include<iostream>
#include<iomanip>
#include<vector>
#include<string>
#include<algorithm>
#include<cmath>
#include<functional>
#include<map>
#include<cstdlib>
#include<complex>
#include<numeric>
#include<set>
#include<deque>
#include<queue>

using namespace std;
using Int = long long;
Int INF = 1LL << 60;
const Int MOD = 1000000000 + 7;

Int N;
string S;

template<int Modulo = MOD> struct Mint {
	Int val;
	constexpr Mint(Int v = 0) noexcept : val(v% Modulo) {
		if (val < 0) v += Modulo;
	}
	constexpr int getmod() { return Modulo; }
	constexpr Mint operator - () const noexcept { return val ? Modulo - val : 0; }
	constexpr Mint operator + (const Mint& r) const noexcept { return Mint(*this) += r; }
	constexpr Mint operator - (const Mint& r) const noexcept { return Mint(*this) -= r; }
	constexpr Mint operator * (const Mint& r) const noexcept { return Mint(*this) *= r; }
	constexpr Mint operator / (const Mint& r) const noexcept { return Mint(*this) /= r; }
	constexpr Mint& operator += (const Mint& r) noexcept {
		val += r.val;
		if (val >= Modulo) val -= Modulo;
		return *this;
	}
	constexpr Mint& operator -= (const Mint& r) noexcept {
		val -= r.val;
		if (val < 0) val += Modulo;
		return *this;
	}
	constexpr Mint& operator *= (const Mint& r) noexcept {
		val = val * r.val % Modulo;
		return *this;
	}
	constexpr Mint& operator /= (const Mint& r) noexcept {
		Int a = r.val, b = Modulo, u = 1, v = 0;
		while (b) {
			Int t = a / b;
			a -= t * b; swap(a, b);
			u -= t * v; swap(u, v);
		}
		val = val * u % Modulo;
		if (val < 0) val += Modulo;
		return *this;
	}
	constexpr bool operator == (const Mint& r) const noexcept {
		return this->val == r.val;
	}
	constexpr bool operator != (const Mint& r) const noexcept {
		return this->val != r.val;
	}
	friend ostream& operator << (ostream& os, const Mint<Modulo>& x) noexcept {
		return os << x.val;
	}
	friend istream& operator >> (istream& is, Mint<Modulo>& x) noexcept {
		Int tmp; is >> tmp;
		x = Mint<Modulo>(tmp);
		return is;
	}
	friend constexpr Mint<Modulo> modpow(const Mint<Modulo>& a, Int n) noexcept {
		Mint res(1), tmp = a;
		while (n > 0) {
			if (n & 1) res *= tmp;
			tmp *= tmp;
			n >>= 1;
		}
		return res;
	}
};

using mint = Mint<>;

class Rolling_hash {

private:
	using Mint1 = Mint<2147483647>;
	using Mint2 = Mint<2147483629>;

	vector<Mint1> hash1, pow1;
	vector<Mint2> hash2, pow2;
	const int base1 = 2147483634;
	const int base2 = 2147483627;
	int sz;

public:
	Rolling_hash(const string& s) :sz(s.size()) {

		hash1.assign(sz + 1, 0); pow1.assign(sz + 1, 1);
		hash2.assign(sz + 1, 0); pow2.assign(sz + 1, 1);

		for (int i = 0; i < sz; i++) {
			hash1[i + 1] = hash1[i] * base1 + s[i];
			pow1[i + 1] = pow1[i] * base1;
			hash2[i + 1] = hash2[i] * base2 + s[i];
			pow2[i + 1] = pow2[i] * base2;
		}
	}

	pair<int, int> get(int l, int r) {
		int res1 = (hash1[r] - hash1[l] * pow1[r - l]).val;
		int res2 = (hash2[r] - hash2[l] * pow2[r - l]).val;
		return { res1, res2 };
	}
};

bool isOK(Int x) {//長さxが答えを満たすがどうか
	
	Rolling_hash a(S);
	pair<Int,Int> tempi, tempj;

	for (Int i = 0; i < N; i++) {
		if (i+x-1>=N) {
			break;
		}
		tempi = a.get(i, i + x);

		for (Int j = i+x; j < N; j++) {
			if (j+x-1>=N) {
				break;
			}
			tempj = a.get(j, j + x);
			if (tempi.first==tempj.first&&tempi.second==tempj.second) {
				return true;
			}
		}
	}

	return false;
}

Int binary_search() {
	Int ng =N/2+1;
	Int ok =-1 ;

	while (abs(ok - ng) > 1) {
		Int mid = (ok + ng) / 2;

		if (isOK(mid)) ok = mid;
		else ng = mid;
	}
	return ok;
}

int main() {

	cin.tie(nullptr);
	ios::sync_with_stdio(false);

	cin >> N >> S;

	

	Int ans = binary_search();

	cout << ans << "\n";

}


Measured memory_bytes: 528