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

// def
#define debug(x) cout << #x << ": " << x << endl
#define out(x) cout << x << endl
#define repeat(i, a, b) for (int i = (a); i < (b); i++)
#define revrepeat(i, a, b) for (int i = (b)-1; i >= (a); i--)
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define revrep(i, n) for (int i = (n)-1; i >= 0; i--)
#define all(x) (x).begin(), (x).end()
#define CYES cout << "Yes" << endl
#define CNO cout << "No" << endl
#define SPC(x) cout << fixed << setprecision(x)
#define ZERO(a) memset(a, 0, sizeof(a))
#define MINUS(a) memset(a, 0xff, sizeof(a))

typedef long long ll;
typedef long double ld;
typedef vector<int> vi;
typedef vector<vector<int>> vii;
typedef vector<ll> vl;
typedef vector<vector<ll>> vll;
typedef pair<int, int> P;
typedef complex<ld> Point;
const int MOD = 1e9 + 7;
const int INF = 1e9;
const int MAX_V = 1e5 + 5;
const int MAX_N = 1e5 + 5;
const double PI = acos(-1);

// MOD固定,奇数乱択ローリングハッシュ
// import from https://qiita.com/keymoon/items/11fac5627672a6d6a9f6
struct RollingHash {
    const unsigned long long MASK30 = (1ULL << 30) - 1;
    const unsigned long long MASK31 = (1ULL << 31) - 1;
    const unsigned long long MODL = (1ULL << 61) - 1;
    const unsigned long long POSITIVIZER = MODL * ((1ULL << 3) - 1);
    const unsigned long long MAX_LENGTH = 500000ULL;
    unsigned int Base;
    vector<unsigned long long> powMemo;
    vector<unsigned long long> hash;

    RollingHash(string s) : powMemo(MAX_LENGTH + 1), hash(s.length() + 1) {
        // 基数を乱数で決定 129 <= Base <= INT_MAX
        unsigned int mi = 129;
        unsigned int range = INT_MAX - mi + 1;
        srand(time(NULL)); // srand でシード値を固定する。同一実行であれば複数の RollingHash が同じ基数を持てる。
        Base = (unsigned int)(rand() % range + mi);
        // powMemo 初期化
        powMemo[0] = 1;
        for (int i = 1; i < (int)powMemo.size(); i++)
            powMemo[i] = CalcMod(Mul(powMemo[i - 1], Base));
        // hashTable 初期化
        for (int i = 0; i < (int)s.length(); i++)
            hash[i + 1] = CalcMod(Mul(hash[i], Base) + s[i]);
    }

    // Hash 値を求める関数
    unsigned long long GetHash(int begin, int length) {
        return CalcMod(hash[begin + length] + POSITIVIZER - Mul(hash[begin], powMemo[length]));
    }

    // オーバーフロー対策
    unsigned long long Mul(unsigned long long l, unsigned long long r) {
        auto lu = l >> 31;
        auto ld = l & MASK31;
        auto ru = r >> 31;
        auto rd = r & MASK31;
        auto middleBit = ld * ru + lu * rd;
        return ((lu * ru) << 1) + ld * rd + ((middleBit & MASK30) << 31) + (middleBit >> 30);
    }

    unsigned long long Mul(unsigned long long l, unsigned int r) {
        auto lu = l >> 31;
        auto rd = r & MASK31;
        auto middleBit = lu * rd;
        return (l & MASK31) * rd + ((middleBit & MASK30) << 31) + (middleBit >> 30);
    }

    // mod 2^61-1を計算する関数
    unsigned long long CalcMod(unsigned long long val) {
        val = (val & MODL) + (val >> 61);
        if (val >= MODL) val -= MODL;
        return val;
    }

    // S[a:], T[b:] の LCP (Longest Common Prefix) を求める
    // O(log N)
    unsigned int GetLCP(int a, int b) {
        int len = min((int)hash.size() - a, (int)hash.size() - b);
        int low = 0, high = len;
        while (high - low > 1) {
            int mid = (low + high) >> 1;
            if (GetHash(a, mid) != GetHash(b, mid))
                high = mid;
            else
                low = mid;
        }
        return low;
    }
};

int main() {
    int n;
    string s;
    cin >> n >> s;
    RollingHash rh(s);

    int res = 0;
    rep(i, s.size()) {
        repeat(j, i + 1, s.size()) {
            int lcp = rh.GetLCP(i, j);
            lcp = min(lcp, j - i);
            res = max(res, lcp);
        }
    }
    out(res);
}


Measured memory_bytes: 4224