#include <bits/stdc++.h>

using namespace std;

template<typename S, typename T>
ostream& operator<<(ostream& out, const pair<S, T> p) {
    out << "(" << p.first << "," << p.second << ")";
    return out;
}

template<typename T>
ostream& operator<<(ostream& out, const vector<T>& v) {
    for (auto a: v)
        out << a << " ";
    return out;
}

template<typename T>
ostream& operator<<(ostream& out, const set<T>& S) {
    for (auto a: S)
        cout << a << " ";
    return out;
}

template<typename T>
ostream& operator<<(ostream& out, const multiset<T>& S) {
    for (auto a: S)
        cout << a << " ";
    return out;
}

template<typename S, typename T>
ostream& operator<<(ostream& out, const map<S, T>& M) {
    for (auto m: M)
        cout << "(" << m.first << "->" << m.second << ") ";
    return out;
}

template<typename S, typename T>
pair<S, T> operator+(pair<S, T> a, pair<S, T> b) {
    return make_pair(a.first + b.first, a.second + b.second);
}

template<typename S, typename T>
pair<S, T> operator-(pair<S, T> a, pair<S, T> b) {
    return make_pair(a.first - b.first, a.second - b.second);
}

struct RMQ {
    int n;
    int m;
    vector<vector<int>> A;

    void init(vector<int>& B) {
        n = B.size();
        m = floor(log2(n)) + 1;

        A.resize(m);
        for (int i = 0; i < m; i++)
            A[i].resize(n);

        copy(B.begin(), B.end(), A[0].begin());

        for (int i = 1; i < m; i++) {
            for (int j = 0; j + (1<<i) <= n; j++) {
                A[i][j] = min(A[i-1][j], A[i-1][j+(1<<(i-1))]);
            }
        }
    }

    int query(int x, int y) {
        int len = (y - x + 1);
        int m = floor(log2(len));

        return min(A[m][x], A[m][y-(1<<m)+1]);
    }
};

struct SuffixArray {
    int n;
    string T;

    vector<int> sa;
    vector<int> rank;

    // instance only when build_lcp is enabled
    vector<int> lcp;
    RMQ rmq;

    // counting sort
    int sigma;
    vector<vector<int>> bucket;
    vector<int> tmp;
    int gap;

    void counting_sort(int k) {
        int m = max(n + 1, sigma + 1);

        bucket.resize(m);
        for (int i = 0; i < m; i++)
            bucket[i].clear();

        for (auto a: sa) {
            if (a + k >= n)
                bucket[0].push_back(a);
            else
                bucket[rank[a+k]+1].push_back(a);
        }

        sa.clear();
        for (auto b: bucket) {
            for (auto a: b) {
                sa.push_back(a);
            }
        }
    }

    void init(int sigma, string& str, bool build_lcp) {
        this->sigma = sigma;
        n = str.size();
        T = str;

        sa.resize(n);
        tmp.resize(n);
        rank.resize(n);

        for (int i = 0; i < n; i++)
            sa[i] = i;

        for (int i = 0; i < n; i++)
            rank[i] = str[i] - 'a';

        auto my_cmp = [&](int a, int b) {
            if (rank[a] != rank[b])
                return rank[a] < rank[b];

            int val1 = a + gap < n? rank[a+gap]: -1;
            int val2 = b + gap < n? rank[b+gap]: -1;
            return val1 < val2;
        };

        for (gap = 1; gap < n; gap<<=1) {
            counting_sort(gap);
            counting_sort(0);
            tmp[sa[0]] = 0;
            for (int i = 1; i < n; i++) {
                tmp[sa[i]] = tmp[sa[i-1]];
                if (my_cmp(sa[i-1], sa[i]))
                    tmp[sa[i]]++;
            }
            copy(tmp.begin(), tmp.end(), rank.begin());

            if (rank[sa.back()] == n - 1)
                break;
        }

        if (build_lcp) {
            lcp.resize(n);
            for (int i = 0, k = 0; i < n; i++) {
                if (k)
                    k--;
                if (rank[i] == 0) {
                    k = 0;
                } else {
                    while (str[i+k] == str[sa[rank[i]-1]+k])
                        k++;
                }
                lcp[rank[i]] = k;
            }

            rmq.init(lcp);
        }
    }

    int lower_bound(string& P) {
        int m = P.size();
        int left = 0;
        int right = n - 1;

        if (T.substr(sa[right]) < P)
            return n;

        if (T.substr(sa[left]) >= P)
            return 0;

        while (right - left > 1) {
            int mid = (left + right) / 2;
            if (T.substr(sa[mid]) < P)
                left = mid;
            else
                right = mid;
        }

        string tmp = T.substr(sa[right]);
        if (tmp.size() >= m && tmp.substr(0, m) == P)
            return right;

        return n;
    }

    int upper_bound(string& P) {
        int first = lower_bound(P);
        if (first == n)
            return n;

        int m = P.size();
        int left = first + 1;
        int right = n - 1;

        if (first + 1 == n)
            return n;

        if (rmq.query(first + 1, right) >= m)
            return n;

        if (rmq.query(first + 1, left) < m)
            return left;

        while (right - left > 1) {
            int mid = (left + right) / 2;
            if (rmq.query(first + 1, mid) >= m)
                left = mid;
            else
                right = mid;
        }

        return right;
    }

    vector<int> string_matching(string& P) {
        vector<int> ans;
        int first = lower_bound(P);
        int last = upper_bound(P);

        for (int i = first; i < last; i++) {
            ans.push_back(sa[i]);
        }
        sort(ans.begin(), ans.end());

        return ans;
    }
};

SuffixArray suffixArray;

int main() {
    ios::sync_with_stdio(false);
    int n;
    string str;
    cin >> n >> str;
    // cout << "str: " << str << endl;
    suffixArray.init(32, str, true);
    // cout << "sa: " << suffixArray.sa << endl;
    // cout << "rank: " << suffixArray.rank << endl;
    // cout << "lcp: " << suffixArray.lcp << endl;
    int max_len = 0;
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            int s = suffixArray.rank[i];
            int t = suffixArray.rank[j];
            if (s > t)
                swap(s, t);
            int len = suffixArray.rmq.query(s + 1, t);
            len = min(len, j - i);
            max_len = max(max_len, len);
        }
    }
    cout << max_len << endl;
    return 0;
}



Measured memory_bytes: 1152