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

// macros
#define overload_rep(va_0, va_1, va_2, va_3, name, ...) name
#define rep_0_(i, n) for(int i = 0; i < (n); ++i)
#define rep_1_(i, x, y) for(int i = (x); i <= (y); ++i)
#define rep_2_(i, x, y, z) for(int i = (x); i >= (y); --i)
#define rep(...) overload_rep(__VA_ARGS__, rep_2_, rep_1_, rep_0_)(__VA_ARGS__)
#define all(a) a.begin(), a.end()
#ifndef STOP_DEBUG
#define debug(...)\
    do { cerr << boolalpha << "[" << #__VA_ARGS__ << "]:";\
    debug_cerr(__VA_ARGS__); cerr << noboolalpha; } while (0)
#else
#define debug(...)
#endif

// functions
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }
void debug_cerr(){ cerr << '\n'; }
template<class Head, class... Tail>
void debug_cerr(Head head, Tail... tail){
    cerr << ' ' << head;
    debug_cerr(tail...);
}

// cin/cout setup
struct IoSetup{
    IoSetup(){
        cin.tie(nullptr);
        ios::sync_with_stdio(false);
        cout << fixed << setprecision(20);
    }
} iosetup;

// libraries
using ulint = uint_fast64_t;
constexpr ulint RH_MOD = (1ul << 61) - 1;
constexpr ulint MASK30 = (1ul << 30) - 1;
constexpr ulint MASK31 = (1ul << 31) - 1;

constexpr ulint Calc_Mod(const ulint& x) noexcept {
    ulint res = (x & RH_MOD) + (x >> 61);
    if(res > RH_MOD)res -= RH_MOD;
    return res;
}

constexpr ulint Multiply(const ulint& a, const ulint& b) noexcept {
    ulint au = a >> 31, ad = a & MASK31;
    ulint bu = b >> 31, bd = b & MASK31;
    ulint mid = ad * bu + au * bd, midu = mid >> 30, midd = mid & MASK30;
    return Calc_Mod(au * bu * 2 + midu + (midd << 31) + ad * bd);
}

struct Rh_Base {
    uint_fast32_t base1, base2;
    Rh_Base() noexcept {
        srand((uint_fast32_t)clock() + (uint_fast32_t)time(nullptr));
        base1 = rand() % 1024 + 256;
        base2 = rand() % 1024 + 256;
    }
} RH_BASE;

struct RollingHash {
private:

    string str;
    vector<ulint> hash1, hash2, pow1, pow2;

public:

    RollingHash() noexcept {}
    RollingHash(const string &str_) noexcept { init(str_); }
    ~RollingHash() noexcept {}

    void init(const string& str_) noexcept {
        size_t sz = str_.size();
        str = str_;
        hash1.assign(sz + 1, 0); hash2.assign(sz + 1, 0);
        pow1.assign(sz + 1, 1); pow2.assign(sz + 1, 1);
        for(size_t i = 0; i < sz; i++){
            hash1[i + 1] = Calc_Mod(Multiply(hash1[i], RH_BASE.base1) + str_[i]);
            hash2[i + 1] = Calc_Mod(Multiply(hash2[i], RH_BASE.base2) + str_[i]);
            pow1[i + 1] = Multiply(pow1[i], RH_BASE.base1);
            pow2[i + 1] = Multiply(pow2[i], RH_BASE.base2);
        }
    }

    void add_string(const string& str_) noexcept {
        size_t ns = str.size(), ms = str_.size();
        str += str_;
        hash1.resize(ns + ms + 1); hash2.resize(ns + ms + 1);
        pow1.resize(ns + ms + 1); pow2.resize(ns + ms + 1);
        for(int i = ns; i < ns + ms; i++){
            hash1[i + 1] = Calc_Mod(Multiply(hash1[i], RH_BASE.base1) + str_[i - ns]);
            hash2[i + 1] = Calc_Mod(Multiply(hash2[i], RH_BASE.base2) + str_[i - ns]);
            pow1[i + 1] = Multiply(pow1[i], RH_BASE.base1);
            pow2[i + 1] = Multiply(pow2[i], RH_BASE.base2);
        }
    }

    void add_char(const char& c) noexcept {
        str += c;
        hash1.push_back(Calc_Mod(Multiply(hash1.back(), RH_BASE.base1) + c));
        hash2.push_back(Calc_Mod(Multiply(hash2.back(), RH_BASE.base2) + c));
        pow1.push_back(Multiply(pow1.back(), RH_BASE.base1));
        pow2.push_back(Multiply(pow2.back(), RH_BASE.base2));
    }

    void add_rh(const RollingHash& rh) noexcept {
        add_string(rh.get_str());
    }

    pair<ulint, ulint> get_hash(const size_t& l, const size_t& r) const noexcept {
        ulint res1 = Calc_Mod(hash1[r] + RH_MOD - Multiply(hash1[l], pow1[r - l]));
        ulint res2 = Calc_Mod(hash2[r] + RH_MOD - Multiply(hash2[l], pow2[r - l]));
        return make_pair(res1, res2);
    }

    pair<ulint, ulint> operator () (const size_t& l, const size_t& r) const noexcept {
        return get_hash(l, r);
    }

    size_t size() const noexcept {
        return str.size();
    }

    size_t get_lcp(const size_t& l, const size_t& r) const noexcept {
        size_t ok = 0, ng = min(size() - l, size() - r) + 1;
        while(ng - ok > 1){
            size_t md = (ok + ng) >> 1;
            (get_hash(l, l + md) == get_hash(r, r + md) ? ok : ng) = md;
        }
        return ok;
    }

    string get_str() const noexcept {
        return str;
    }

    char operator [] (const size_t& idx) const noexcept {
        return str[idx];
    }

};

// declarations

// solve
int main(){

    int N;
    string S;
    cin >> N >> S;

    RollingHash rh(S);
    int ok = 0, ng = N;
    while(ng - ok > 1){
        int md = (ok + ng) / 2;
        bool can = [&]{
            map<pair<ulint, ulint>, int> mp;
            rep(i, N - md + 1){
                pair<ulint, ulint> got = rh(i, i + md);
                if(mp.find(got) == mp.end())mp[got] = i;
                else if(mp[got] + md <= i)return true;
            }
            return false;
        }();
        (can ? ok : ng) = md;
    }

    cout << ok << '\n';
    return 0;

}


Measured memory_bytes: 768