// need
#include <iostream>
#include <algorithm>
// data structure
#include <bitset>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <complex>
//#include <deque>
#include <valarray>
#include <unordered_map>
#include <unordered_set>
#include <array>
// etc
#include <cassert>
#include <cmath>
#include <functional>
#include <iomanip>
#include <chrono>
#include <random>
#include <numeric>

// input
#define INIT                          \
    std::ios::sync_with_stdio(false); \
    std::cin.tie(0);
#define VAR(type, ...) \
    type __VA_ARGS__;  \
    MACRO_VAR_Scan(__VA_ARGS__);
template <typename T>
void MACRO_VAR_Scan(T &t) { std::cin >> t; }
template <typename First, typename... Rest>
void MACRO_VAR_Scan(First &first, Rest &... rest)
{
    std::cin >> first;
    MACRO_VAR_Scan(rest...);
}
#define VEC_ROW(type, n, ...)                \
    std::vector<type> __VA_ARGS__;           \
    MACRO_VEC_ROW_Init(n, __VA_ARGS__);      \
    for (int w_ = 0; w_ < n; ++w_)           \
    {                                        \
        MACRO_VEC_ROW_Scan(w_, __VA_ARGS__); \
    }
template <typename T>
void MACRO_VEC_ROW_Init(int n, T &t)
{
    t.resize(n);
}
template <typename First, typename... Rest>
void MACRO_VEC_ROW_Init(int n, First &first, Rest &... rest)
{
    first.resize(n);
    MACRO_VEC_ROW_Init(n, rest...);
}
template <typename T>
void MACRO_VEC_ROW_Scan(int p, T &t) { std::cin >> t[p]; }
template <typename First, typename... Rest>
void MACRO_VEC_ROW_Scan(int p, First &first, Rest &... rest)
{
    std::cin >> first[p];
    MACRO_VEC_ROW_Scan(p, rest...);
}
#define VEC(type, c, n)     \
    std::vector<type> c(n); \
    for (auto &i : c)       \
        std::cin >> i;
#define MAT(type, c, m, n)                                     \
    std::vector<std::vector<type>> c(m, std::vector<type>(n)); \
    for (auto &R : c)                                          \
        for (auto &w : R)                                      \
            std::cin >> w;
// output
#define OUT(dist) std::cout << (dist);
#define FOUT(n, dist) std::cout << std::fixed << std::setprecision(n) << (dist);
#define SOUT(n, c, dist) std::cout << std::setw(n) << std::setfill(c) << (dist);
#define SP std::cout << " ";
#define TAB std::cout << "\t";
#define BR std::cout << "\n";
#define SPBR(w, n) std::cout << (w + 1 == n ? '\n' : ' ');
#define ENDL std::cout << std::endl;
#define FLUSH std::cout << std::flush;
#define SHOW(dist)                                     \
    {                                                  \
        std::cerr << #dist << "\t:" << (dist) << "\n"; \
    }
#define SHOWVECTOR(v)                \
    {                                \
        std::cerr << #v << "\t:";    \
        for (const auto &xxx : v)    \
        {                            \
            std::cerr << xxx << " "; \
        }                            \
        std::cerr << "\n";           \
    }
#define SHOWVECTOR2(v)                   \
    {                                    \
        std::cerr << #v << "\t:\n";      \
        for (const auto &xxx : v)        \
        {                                \
            for (const auto &yyy : xxx)  \
            {                            \
                std::cerr << yyy << " "; \
            }                            \
            std::cerr << "\n";           \
        }                                \
    }
#define SHOWQUEUE(a)                         \
    {                                        \
        auto tmp(a);                         \
        std::cerr << #a << "\t:";            \
        while (!tmp.empty())                 \
        {                                    \
            std::cerr << tmp.front() << " "; \
            tmp.pop();                       \
        }                                    \
        std::cerr << "\n";                   \
    }
#define SHOWSTACK(a)                       \
    {                                      \
        auto tmp(a);                       \
        std::cerr << #a << "\t:";          \
        while (!tmp.empty())               \
        {                                  \
            std::cerr << tmp.top() << " "; \
            tmp.pop();                     \
        }                                  \
        std::cerr << "\n";                 \
    }
// utility
#define ALL(a) (a).begin(), (a).end()
#define FOR(w, a, n) for (int w = (a); w < (n); ++w)
#define RFOR(w, a, n) for (int w = (n)-1; w >= (a); --w)
#define REP(w, n) for (int w = 0; w < int(n); ++w)
#define RREP(w, n) for (int w = int(n) - 1; w >= 0; --w)
#define IN(a, x, b) (a <= x && x < b)
template <class T>
inline T CHMAX(T &a, const T b)
{
    return a = (a < b) ? b : a;
}
template <class T>
inline T CHMIN(T &a, const T b) { return a = (a > b) ? b : a; }
// test
template <class T>
using V = std::vector<T>;
template <class T>
using VV = V<V<T>>;

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

#define random_shuffle "USE std::shuffle!";

// type/const
#define int ll
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using PAIR = std::pair<int, int>;
using PAIRLL = std::pair<ll, ll>;
constexpr int INFINT = (1 << 30) - 1;                   // 1.07x10^ 9
constexpr int INFINT_LIM = (1LL << 31) - 1;             // 2.15x10^ 9
constexpr ll INFLL = 1LL << 60;                         // 1.15x10^18
constexpr ll INFLL_LIM = (1LL << 62) - 1 + (1LL << 62); // 9.22x10^18
constexpr double EPS = 1e-10;
constexpr int MOD = 1000000007;
constexpr double PI = 3.141592653589793238462643383279;
template <class T, size_t N>
void FILL(T (&a)[N], const T &val)
{
    for (auto &x : a)
        x = val;
}
template <class ARY, size_t N, size_t M, class T>
void FILL(ARY (&a)[N][M], const T &val)
{
    for (auto &b : a)
        FILL(b, val);
}
template <class T>
void FILL(std::vector<T> &a, const T &val)
{
    for (auto &x : a)
        x = val;
}
template <class ARY, class T>
void FILL(std::vector<std::vector<ARY>> &a, const T &val)
{
    for (auto &b : a)
        FILL(b, val);
}
// ------------>8------------------------------------->8------------

struct HashData
{
    int h1, h2;
    bool operator==(const HashData &rhs) const
    {
        return h1 == rhs.h1 && h2 == rhs.h2;
    }
    bool operator!=(const HashData &rhs) const { return !(*this == rhs); }
};
class RollingHash
{
    template <int B, int M>
    class RollingHashPart
    {
    private:
        size_t len;
        std::vector<int> pow_b, hash;

    public:
        RollingHashPart() {}
        template <typename T>
        RollingHashPart(const T &s) : len(s.size()), pow_b(s.size() + 1), hash(s.size() + 1)
        {
            pow_b[0] = 1;
            hash[0] = 0;
            for (int i = 0; i < len; i++)
            {
                pow_b[i + 1] = (ll)pow_b[i] * B % M;
                hash[i + 1] = ((ll)hash[i] * B + s[i]) % M;
            }
        }
        // [l, r)
        int get(int l, int r) const
        {
            int res = (hash[r] - (ll)hash[l] * pow_b[r - l]) % M;
            if (res < 0)
                res += M;
            return res;
        }
    };

public:
    RollingHashPart<1033, 1000000007> rh1;
    RollingHashPart<6037, 999999937> rh2;

    RollingHash() {}
    template <typename T>
    RollingHash(const T &s) : rh1(s), rh2(s) {}
    // [l, r)
    HashData get(int l, int r) const
    {
        return HashData{rh1.get(l, r), rh2.get(l, r)};
    }
    // [l1, r1), [l2, r2)
    int lcp(const RollingHash &rhs, int l1, int r1, int l2, int r2) const
    {
        int lb = 0, ub = std::min(r1 - l1, r2 - l2) + 1;
        while (ub - lb > 1)
        {
            int md = (lb + ub) >> 1;
            (get(l1, l1 + md) == rhs.get(l2, l2 + md) ? lb : ub) = md;
        }
        return lb;
    }
};

signed main()
{
    INIT;

    VAR(int, n);
    VAR(std::string, s);

    RollingHash rh(s);
    auto check = [&](int d) {
        REP(i, n)
        FOR(j, i + d, n - d + 1)
        {
            if (s[i] != s[j])
                continue;
            if (rh.get(i, i + d) == rh.get(j, j + d))
                return true;
        }
        return false;
    };

    int ok = 0, ng = n / 2 + 2;
    while (ng - ok > 1)
    {
        int me = (ok + ng) / 2;
        if (check(me))
            ok = me;
        else
            ng = me;
    }
    OUT(ok)
    BR;

    return 0;
}


Measured memory_bytes: 384