#include<iostream>
#include<string>
#include<vector>
#include<cassert>
#define rep(i, start, end) for (int i = (int)start; i < (int)end; ++i)
#define rrep(i, start, end) for (int i = (int)start - 1; i >= end; --i)
#define arep(i, c) for (auto i = (c).begin(); i != (c).end(); ++i)
#define all(x) (x).begin(), (x).end()
using namespace std;

template<typename ITERATOR>
class KMP {
    private:
        ITERATOR pattern;  // iterator
        vector<int> shift_table;
    public:
        KMP(ITERATOR _pattern):pattern(_pattern) {
            // calculate tagged border
            vector<int> tagged_border(pattern.size() + 1);
            tagged_border[0] = -1;  // -1 means that there is no border
            int prev_border = -1;
            for (int i = 0; i < pattern.size(); ++i) {
                while (prev_border >= 0 && pattern[prev_border] != pattern[i]) {
                    prev_border = tagged_border[prev_border];
                }
                prev_border++;
                if (i + 1 < pattern.size() && pattern[i + 1] == pattern[prev_border]) {
                    tagged_border[i + 1] = tagged_border[prev_border];
                }
                else tagged_border[i + 1] = prev_border;
            }
            shift_table = tagged_border;
        }
        vector<int> get_shift_table() {
            return shift_table;
        }
        vector<int> search(ITERATOR target) {
            assert(target.size() >= pattern.size());
            // return matching position
            vector<int> res;
            int target_size = target.size(), pattern_size = pattern.size();
            int target_ptr = 0, pattern_ptr = 0;
            while (target_ptr < target_size) {
                if (target[target_ptr] == pattern[pattern_ptr]) {
                    target_ptr++;
                    pattern_ptr++;
                }
                else {
                    if (pattern_ptr == 0) target_ptr++;
                    else {
                        if (shift_table[pattern_ptr] < 0) {
                            pattern_ptr = 0;
                        }
                        else pattern_ptr = shift_table[pattern_ptr];
                    }
                }
                if (pattern_ptr == pattern_size) {
                    res.push_back(target_ptr - pattern_ptr);
                    if (shift_table[pattern_ptr] < 0) {
                        pattern_ptr = 0;
                    }
                    else pattern_ptr = shift_table[pattern_ptr];
                }
            }
            return res;
        }
};

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int N;
    string S;
    cin >> N >> S;
    int l = 0, r = N;
    while (r - l > 1) {
        int mid = (l + r) / 2;
        bool is_ok = false;
        rep(i, 0, N - mid + 1) {
            if (N - i - mid < mid) break;
            KMP<string> kmp(S.substr(i, mid));
            vector<int> matched = kmp.search(S.substr(i + mid, N - i - mid));
            if (matched.size() > 0) {
                is_ok = true;
                break;
            }
        }
        if (is_ok) l = mid;
        else r = mid;
    }
    cout << l << endl;
    return 0;
}

Measured memory_bytes: 384