/*
https://atcoder.jp/contests/abc141/submissions/8538616
*/

#define NDEBUG
#include <cassert>
#include <cstdint>
#include <iterator>
#include <vector>

namespace n91 {

class m61_rolling_hash {
private:
  using u64 = std::uint_fast64_t;
  using size_t = std::size_t;

public:
  using value_type = u64;

private:
  static constexpr u64 pow2(const int exp) noexcept {
    return static_cast<u64>(1) << exp;
  }

  static constexpr u64 Mod = (static_cast<u64>(1) << 61) - 1;

  static constexpr u64 multiplies_plus(const u64 l, const u64 r,
                                       const u64 c) noexcept {
    const u64 ld = l % pow2(31);
    const u64 lu = l / pow2(31);
    const u64 rd = r % pow2(31);
    const u64 ru = r / pow2(31);
    const u64 m = ld * ru + lu * rd;
    u64 v = lu * ru * 2 + ld * rd + m % pow2(30) * pow2(31) + m / pow2(30) + c;
    v = v % pow2(61) + v / pow2(61);
    if (v >= Mod) {
      v -= Mod;
    }
    return v;
  }

  std::vector<u64> prefix;
  std::vector<u64> power;

public:
  m61_rolling_hash() = default;
  template <class C>
  explicit m61_rolling_hash(const C &c, const u64 base = 91)
      : prefix(), power() {
    const size_t n = c.size();
    prefix.reserve(n + 1);
    prefix.push_back(0);
    for (size_t i = 0; i != n; ++i) {
      prefix.push_back(multiplies_plus(prefix.back(), base, c[i]));
    }
    power.reserve(n + 1);
    power.push_back(Mod - 1);
    for (size_t i = 0; i != n; ++i) {
      power.push_back(multiplies_plus(power.back(), base, 0));
    }
  }

  u64 get(const size_t first, const size_t last) const noexcept {
    assert(first <= last);
    assert(last < prefix.size());
    return multiplies_plus(prefix[first], power[last - first], prefix[last]);
  }
  u64 operator()(const size_t first, const size_t last) const noexcept {
    return get(first, last);
  }

  size_t get_lcp(const size_t &l, const size_t &r) const noexcept {
    size_t ok = 0, ng = std::min(prefix.size() - l, prefix.size() - r);
    while (ng - ok > 1) {
      size_t md = (ok + ng) >> 1;
      (get(l, l + md) != get(r, r + md) ? ng : ok) = md;
    }
    return ok;
  }
};
constexpr m61_rolling_hash::u64 m61_rolling_hash::Mod;

} // namespace n91

#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) ? ng : ok) = 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;

    n91::m61_rolling_hash rh(S);
    size_t ans = 0;
    rep(i, N)rep(j, i){
        size_t lcp = rh.get_lcp(i, j);
        chmin<size_t>(lcp, i - j);
        chmax(ans, lcp);
    }
    cout << ans << '\n';

}


Measured memory_bytes: 384