#pragma GCC optimize "O3"
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>

#define POP pop_back
#define MP make_pair
#define MT make_tuple
#define PB push_back
#define F first
#define S second
#define X real()
#define Y imag()
#ifndef LOCAL
    #define endl "\n"
#endif

using namespace std;
using namespace chrono;
using namespace __gnu_pbds;

typedef pair<int, int> ii;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> pll;
typedef long long C;
typedef complex<C> P;

const int INF = 1e9;
const long long LLINF = 1e18;
const double PI = 3.14159265358979323;
const long long PRIME = (1ll << 31) - 1;
const long long PRIME2 = 269;
const long long MOD = 1e9 + 7;

template <class T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
template <class T, class U> using ordered_map = tree<T, U, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
template <class T> using hash_set = gp_hash_table<T, null_type>;
template <class T, class U> using hash_map = gp_hash_table<T, U>;
template <class T, class U> ostream& operator<< (ostream &out, pair<T, U> p) { return out << "(" << p.F << ", " << p.S << ")"; }
template <class T> ostream& operator<< (ostream &out, vector<T> c) { for (auto it = c.begin(); it != c.end(); ++it) { if (it != c.begin()) out << " "; out << *it; } return out; }
template <class T> ostream& operator<< (ostream &out, set<T> c) { for (auto it = c.begin(); it != c.end(); ++it) { if (it != c.begin()) out << " "; out << *it; } return out; }
template <class T, class U> ostream& operator<< (ostream &out, map<T, U> c) { for (auto it = c.begin(); it != c.end(); ++it) { if (it != c.begin()) out << " "; out << *it; } return out; }
template <class T> ostream& operator<< (ostream &out, ordered_set<T> c) { for (auto it = c.begin(); it != c.end(); ++it) { if (it != c.begin()) out << " "; out << *it; } return out; }
template <class T, class U> ostream& operator<< (ostream &out, ordered_map<T, U> c) { for (auto it = c.begin(); it != c.end(); ++it) { if (it != c.begin()) out << " "; out << *it; } return out; }
template <class T> ostream& operator<< (ostream &out, hash_set<T> c) { for (auto it = c.begin(); it != c.end(); ++it) { if (it != c.begin()) out << " "; out << *it; } return out; }
template <class T, class U> ostream& operator<< (ostream &out, hash_map<T, U> c) { for (auto it = c.begin(); it != c.end(); ++it) { if (it != c.begin()) out << " "; out << *it; } return out; }
template <class T> int sign (T x) { return (x > 0) - (x < 0); }
inline ll mod (ll a, ll b) { return a%b + (a < 0) * b; }
inline ll mod (ll a) { return mod(a, MOD); }
C dot (P a, P b) { return a.X * b.X + a.Y * b.Y; }
C cross (P a, P b) { return a.X * b.Y - b.X * a.Y; }

double startTime;
double currTime() { return duration_cast<nanoseconds>(system_clock::now().time_since_epoch()).count() / 1e9; }
double execTime() { return currTime() - startTime; }

vector<int> kmp (string s) {
    vector<int> v(s.size() + 1, -1);
    for (int i = 0; i < s.size(); i++) {
        int back = v[i];
        while (back >= 0 && s[back] != s[i])
            back = v[back];
        v[i + 1] = back + 1;
    }
    return v;
}

vector<ll> compute_pows (ll base, int n, ll modulo) {
    vector<ll> pows(n + 1);
    pows[0] = mod(1, modulo);
    for (int i = 0; i < n; i++)
        pows[i + 1] = mod(pows[i] * base, modulo);
    return pows;
}

vector<ll> prefix_hash (string s, ll abc, ll modulo) {
    vector<ll> hashes(s.size() + 1);
    for (int i = 0; i < s.size(); i++)
        hashes[i + 1] = mod(hashes[i] * abc + s[i], modulo);
    return hashes;
}

void prefix_hash (string s, int &tests, vector<vector<ll>> &pows, vector<vector<ll>> &hashes, vector<ll> &modulo) {
    tests = 6;
    pows.resize(tests);
    hashes.resize(tests);
    vector<ll> abc{ 269, 269, 269, 271, 271, 271 };
    modulo = vector<ll>{ 1000000007, 1000000009, 2147483647, 1000000007, 1000000009, 2147483647 };
    for (int i = 0; i < tests; i++) {
        pows[i] = compute_pows(abc[i], s.size(), modulo[i]);
        hashes[i] = prefix_hash(s, abc[i], modulo[i]);
    }
}

ll compute_hash (vector<ll> &pows, vector<ll> &hashes, ll modulo, int l, int r) {
    return mod(hashes[r] - hashes[l] * pows[r - l], modulo);
}

vector<ll> compute_hash (int &tests, vector<vector<ll>> &pows, vector<vector<ll>> &hashes, vector<ll> &modulo, int l, int r) {
    vector<ll> res(tests);
    for (int i = 0; i < tests; i++)
        res[i] = compute_hash(pows[i], hashes[i], modulo[i], l, r);
    return res;
}

int main() {
#ifndef LOCAL
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
#endif
    srand(time(0));
    startTime = currTime();

    int n;
    cin >> n;

    string s;
    cin >> s;

    int tests;
    vector<vector<ll>> pows, hashes;
    vector<ll> modulo;
    prefix_hash(s, tests, pows, hashes, modulo);
    int l = 0, r = n;
    while (r - l > 1) {
        int len = (l + r)/2;
        bool ok = false;
        map<vector<ll>, int> m;
        for (int i = 0; i < n - len + 1; i++) {
            vector<ll> my_hash = compute_hash(tests, pows, hashes, modulo, i, i + len);
            if (m.find(my_hash) == m.end())
                m[my_hash] = i;
            else if (i - m[my_hash] >= len)
                ok = true;
        }
        if (ok)
            l = len;
        else
            r = len;
    }
    cout << l << endl;
}

Measured memory_bytes: 1408