#include <bits/stdc++.h>
using namespace std;

#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define MIN(a) *min_element(all(a))
#define MAX(a) *max_element(all(a))
#define SUM(a) accumulate(all(a), 0LL)
#define REP(i, n) for(int (i)=0; (i)<(n); (i)++)
#define RREP(i, n) for(int (i)=(n)-1; (i)>=0; (i)--)
#define FOR(i, m, n) for(int (i)=(m); (i)<(n); i++)
#define FORR(i, m, n) for(int (i)=(n)-1; (i)>=(m); i--)
#define debug(x) cerr << #x << " = " << x << endl
//#define int long long

typedef long long ll;
//ll const MOD = 998244353;
ll const MOD = 1e9+7;
int const inf = 1e9;
ll const INF = 1e18;

inline void ios_(){cin.tie(0); ios::sync_with_stdio(false);}
template<typename T> int size(const T& a){return (int)a.size();}
template<typename T> T Div(T a, T b){return (a + b - 1) / b;}
template<typename T> bool chmin(T& a, const T& b){if(a > b){a = b; return true;} return false;}
template<typename T> bool chmax(T& a, const T& b){if(a < b){a = b; return true;} return false;}

struct RollingHash {
    int N;
    int level;
    string str;
    static vector<ll> base;
    static vector<ll> mod;
    vector<vector<ll>> hash;
    vector<vector<ll>> basepow;

    RollingHash(string s, int lv) : str(s), level(lv) {
        base = {1234, 567};
        mod = {1000000007, 998244353};

        N = s.size();
        hash = vector<vector<ll>>(level, vector<ll>(N+1));

        basepow = vector<vector<ll>>(level, vector<ll>(N+1));

        for(int i=0; i<level; i++) {
            basepow[i][0] = 1;
            for(int j=0; j<N; j++) {
                basepow[i][j+1] = basepow[i][j] * base[i] % mod[i];
            }
        }

        for(int i=0; i<level; i++) {
            for(int j=0; j<N; j++) {
                hash[i][j+1] = hash[i][j];
                hash[i][j+1] += basepow[i][N-1-j] * str[j] % mod[i];
                hash[i][j+1] %= mod[i];
            }
        }
    }

    RollingHash(string s) : RollingHash(s, 1) {}

    // a ** b (mod m)
    ll modpow(ll a, ll b, ll m) {
        ll res = 1;
        a %= m;

        while(b > 0) {
            res *= (b & 1 ? a : 1);
            res %= m;
            b >>= 1;
            a = a * a % m;
        }

        return res;
    }

    // a / b (mod m)
    ll moddiv(ll a, ll b, ll m) {
        return a % m * modpow(b, m - 2, m) % m;
    }

    // [l,r) (0-indexed) の範囲の部分文字列に対するhashを取得
    vector<ll> getSegment(int l, int r) {
        vector<ll> res(level);
        for(int i=0; i<level; i++) {
            res[i] = moddiv((hash[i][r] - hash[i][l] + mod[i]) % mod[i],
                            basepow[i][N - r],
                            mod[i]);
        }
        return res;
    }
};

vector<ll> RollingHash::base;
vector<ll> RollingHash::mod;

ll BinarySearch(ll ng, ll ok, function<bool(ll)> judge){
    while(abs(ok - ng) > 1){
        ll mid = (ok + ng) / 2;
        if(judge(mid)) ok = mid;
        else ng = mid;
    }
    return ok;
}

signed main(){
    int n;
    string s;
    cin >> n >> s;

    RollingHash rh(s, 2);

    auto fn = [&](ll len) {
        map<vector<ll>,int> mp;
        REP(i, n - len + 1){
            vector<ll> h = rh.getSegment(i, i + len);
            if(mp[h] == 0){
                mp[h] = i + 1;
            }else if(i + 1 - mp[h] >= len){
                return true;
            }
        }
        return false;
    };

    cout << BinarySearch(n, 0, fn) << endl;

    return 0;
}


Measured memory_bytes: 1024