// 2^61 - 1
#include <stdio.h>
#include <string>
#include <vector>
#include <array>
#include <random>
#include <map>

class RollingHash{
private:
    #define rep(i, n) for(int (i) = 0; (i) < (n); ++(i))
    using Int = unsigned long long int;
    static constexpr Int mod = (1ULL << 61) - 1;
    static constexpr int L = 31;
    static constexpr Int mask = (1ULL << L) - 1;
    static constexpr int baseLength = 13;
    const std::array<Int, baseLength> base 
                = {3, 5, 7, 11, 13, 17, 801, 893, 931, 1919, 11053, 223197, 3439131};
    static bool unsetKey;
    int key[2];
    const int n;
    std::vector<Int> basePower[2]; // basePower[i][j] := {i番目の基数}^j mod 2^61 - 1
    std::vector<std::pair<Int, Int>> Hash; // Hash[j] := [0.j)のhash値 : {h0, h1}
    std::vector<Int> data;

    // compute a*b % mod in 64bit
    inline Int time_mod(Int a, Int b){
        // a * b = (2^31*au + ad)*(2^31*bu + bd) 
        //       = 2^62 * au*bu + 2^31(ad*bu+au*bd) + ad*bd
        //       (t = ad*bu+au*bd ~> 2^31*tu+td)
        //       = 2^62 * (au*bu + tu) * 2^31*td +ad*bd
        //       = 2 * (au*bu+tu) + 2^31*td + ad*bd
        Int au = a >> L;
        Int ad = a & mask;
        Int bu = b >> L;
        Int bd = b & mask;
        Int  t = ad * bu + au * bd;
        Int tu = t >> L;
        Int td = t & mask;
        Int res = ((au*bu+tu) << 1) + (td << L) + ad*bd;
        divQ(res);
        return res;
    }
    inline void divQ(Int& val){
        val = (val & mod) + (val >> 61);
        if(val >= mod) val -= mod;
    }
public:
    // char配列のコンストラクタ
    RollingHash(char *str): n(strlen(str)){
        data.resize(n); rep(i, n) data[i] = str[i]; setKey(); init();
    }
    RollingHash(std::string& str): n(str.size()){
        data.resize(n); rep(i, n) data[i] = str[i]; setKey(); init();
    }
    
    // keyをセット
    inline void setKey(){
        if(not unsetKey) return;
        std::random_device randRoll;
        key[0] = randRoll() % baseLength;
        do{key[1] = randRoll() % baseLength;}while(key[0] == key[1]);
        unsetKey = false;
    }

    // 初期化　: dataの内容があり, n の値が定まっていること. そして, keyの値も決まっている必要あり
    inline void init(){
        basePower[0].resize(n+1);
        basePower[1].resize(n+1);
        basePower[0][0] = 1; basePower[1][0] = 1;
        rep(i, 2) rep(j, n)
                basePower[i][j+1] = time_mod(basePower[i][j], base[key[i]]);
        

        Hash.resize(n+1);
        Hash[0] = {0, 0};
        rep(j, n){
            Hash[j+1].first  = time_mod(Hash[j].first,  base[key[0]]) + data[j];
            divQ(Hash[j+1].first);
               

            Hash[j+1].second = time_mod(Hash[j].second, base[key[1]]) + data[j];
            divQ(Hash[j+1].second);
        }
    }

    // ハッシュ値の取得 : [left, right) in 0-indexed : 0<=left, right < n
    inline std::pair<Int, Int> getHash(int left, int right){

        std::pair<Int, Int> res;
        res.first = Hash[right].first + mod - time_mod(Hash[left].first, basePower[0][right-left]);
        divQ(res.first);
      
        res.second = Hash[right].second + mod - time_mod(Hash[left].second, basePower[1][right-left]);
        divQ(res.second);
        return res;
    }

    #undef rep
};
bool RollingHash::unsetKey = true;

using lli = long long int;
constexpr int MAX_SIZE = 5e3;
char str[MAX_SIZE + 10];

int main(void){
    int n; scanf("%d%s", &n, str);
    RollingHash RH(str);
    int ok = 0, ng = n/2+1;
    while(ng - ok > 1){
        int mid = (ng + ok) >> 1;
        bool judge = false;
        // 判定
        std::map<std::pair<lli, lli>, int> cnt;
        for(int i = 0; i + mid <= n; ++i){
            std::pair<lli, lli> d = RH.getHash(i, i+mid);
            if(cnt.find(d) == cnt.end()) cnt[d] = i;
            else{
                if(i - cnt[d] >= mid){
                    judge= true;
                    break;
                }
            }
        }
        if(judge) ok = mid;
        else ng = mid;
    }
    printf("%d\n", ok);
    
  
    return 0;
}

Measured memory_bytes: 768