#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#define FOR(i,a,b) for (int i=(a),for_##i##_max=(b);i<=(for_##i##_max);++i)
#define RFOR(i,a,b) for (int i=(a),rfor_##i##_min=(b);i>=(rfor_##i##_min);--i)
#define REP(i,n) for (int i=0,rep_##i##_len=(n);i<(rep_##i##_len);++i)
#define RREP(i,n) for (int i=(n)-1;i>=0;--i)

namespace { // Basics

template<typename T>
void chmin(T &x, T y) {
  x = std::min(x, y);
}

template<typename T>
void chmax(T &x, T y) {
  x = std::max(x, y);
}

template<typename T>
__attribute__((unused))
const auto minimum = static_cast<T const &(*)(T const &, T const &)>(std::min);

template<typename T>
__attribute__((unused))
const auto maximum = static_cast<T const &(*)(T const &, T const &)>(std::max);

template<typename T>
int cmp3(T lhs, T rhs) { return (lhs == rhs) ? 0 : (lhs > rhs) ? 1 : -1; }

template<typename T>
__attribute__((unused))
T constexpr infty = std::numeric_limits<T>::max / 3;
template<>
__attribute__((unused))
auto constexpr infty<int> = 1'100'100'100;
template<>
__attribute__((unused))
auto constexpr infty<long long int> = 100'500'400'300'200'100LL;

using lli = long long int;
using ld = long double;

} // namespace
namespace  { // Mod

template<int mod>
class modulo {
  public:
    constexpr modulo(): num(0) {}
    constexpr modulo(int n): num(n >= 0 ? n%mod : mod-((-n)%mod)) {}
    constexpr modulo(modulo const &) = default;
    constexpr int data() const { return num; }
    constexpr modulo &operator+=(modulo other) {
      num = (num + other.num) % mod;
      return *this;
    }
    constexpr modulo &operator-=(modulo other) {
      num = (num + mod - other.num) % mod;
      return *this;
    }
    constexpr modulo &operator*=(modulo other) {
      long long n = ((long long)num) * ((long long)other.num);
      num = n % mod;
      return *this;
    }
    constexpr modulo &operator/=(modulo other) {
      *this *= other.inverse();
      return *this;
    }
    constexpr static modulo pow(modulo a, int n, modulo res) {
      if (n == 0) { return res; }
      return pow(a*a, n/2, (n&1)?(res*a):res);
    }
    constexpr modulo inverse() const {
      return pow(*this, mod-2, modulo(1));
    }
  private:
    int num;
};

template <int mod>
constexpr bool operator==(modulo<mod> a, modulo<mod> b) {
  return a.data() == b.data();
}

template<int mod>
constexpr modulo<mod> const operator+(modulo<mod> a, modulo<mod> b) {
  a += b;
  return a;
}

template<int mod>
constexpr modulo<mod> const operator-(modulo<mod> a, modulo<mod> b) {
  a -= b;
  return a;
}

template<int mod>
constexpr modulo<mod> const operator*(modulo<mod> a, modulo<mod> b) {
  a *= b;
  return a;
}

template<int mod>
constexpr modulo<mod> const pow(modulo<mod> a, int n) {
  return modulo<mod>::pow(a, n, 1);
}

template<int mod>
constexpr modulo<mod> const operator/(modulo<mod> a, modulo<mod> b) {
  a /= b;
  return a;
}

template<int mod, int size>
struct factorial {
  using Mod = modulo<mod>;
  Mod fact[size];
  Mod invfact[size];
  constexpr factorial(): fact(), invfact() {
    fact[0] = 1;
    for (int i = 1; i < size; ++i) {
      fact[i] = Mod(i) * fact[i-1];
    }
    invfact[size-1] = fact[size-1].inverse();
    for (int i = size - 1; i > 0; --i) {
      invfact[i-1] = invfact[i] * Mod(i);
    }
  }
  Mod const comb(int n, int k) const {
    assert(k <= n && n < size);
    return fact[n] * invfact[k] * invfact[n-k];
  }
};

} // namespace
namespace { // rolling-hash

template <typename T, size_t L>
struct memoized_expt {
  memoized_expt(std::array<T, L> const &base, size_t n): p(base), table(), sz(n) {
    for (size_t z = 0; z < L; ++z) {
      table[z] = std::vector<T>(n+1);
      table[z][0] = static_cast<T>(1);
    }
    for (size_t z = 0; z < L; ++z) for (size_t i = 0; i < n; ++i) {
      table[z][i+1] = table[z][i] * p[z];
    }
  }
  T pow(size_t z, int i) const {
    return table[z][i];
  }
  void extend(size_t diff) {
    for (size_t z = 0; z < L; ++z) for (size_t i = 0; i < diff; ++i) {
      table[z].push_back(table[z].back() * p[z]);
    }
    sz += diff;
  }
  std::array<T, L> p;
  std::array<std::vector<T>, L> table;
  size_t sz;
};

template <typename T, size_t L>
struct sq_expt {
  sq_expt(std::array<T, L> const &base, size_t): p(base) { }
  T pow(size_t z, int k) const {
    T val(1), a(p[z]);
    while (k > 0) {
      if (k & 1) { val *= a; }
      a *= a; k >>= 1;
    }
    return val;
  }
  void extend(size_t) { }
  std::array<T, L> p;
};

template<typename T, size_t L, template <class, size_t> class Pow = memoized_expt>
class rolling_hash {
  public:
    template <typename I>
    explicit rolling_hash(std::string const &s, std::array<I, L> const &base):
      str(s), n(str.size()), pow(convert(base), n), phash() {
      for (size_t z = 0; z < L; ++z) {
        phash[z] = std::vector<T>(n+1);
        phash[z][0] = 0;
        for (size_t i = 0; i < n; ++i) {
          phash[z][i+1] = T(str[i]) + phash[z][i] * pow.p[z];
        }
      }
    }
    size_t size() const { return n; }
    std::string to_string() const { return str; }
    // [i, j)
    std::array<T, L> hash_range(size_t i, size_t j) const {
      std::array<T, L> ret;
      for (size_t z = 0; z < L; ++z) {
        ret[z] = phash[z][j] - phash[z][i] * pow.pow(z, j-i);
      }
      return ret;
    }
    void append(std::string const &s) {
      size_t const len = s.size();
      for (size_t z = 0; z < L; ++z) for (size_t i = 0; i < len; ++i) {
        phash[z].push_back(T(s[i]) + phash[z].back() * pow.p[z]);
      }
      for (auto &&c: s) { str.push_back(c); }
      n += len;
    }
  private:
    template <typename I>
    static std::array<T, L> convert(std::array<I, L> const &v) {
      std::array<T, L> w;
      for (size_t i = 0, n = v.size(); i < n; ++i) {
        w[i] = static_cast<T>(v[i]);
      }
      return w;
    }

  private:
    std::string str;
    size_t n;
    Pow<T, L> pow;
    std::array<std::vector<T>, L> phash;
};

} // namespace

using namespace std;

using Mod = modulo<1'000'000'009>;

void solve(int N, string const &S) {
  array<int, 2> arr = {999999883, 999999929};
  auto const rh = rolling_hash<Mod, 2>(S, arr);
  int lb = 0, ub = N;
  while (ub - lb > 1) {
    int len = (lb + ub) / 2;
    bool possible = false;
    for (int i = 0; i <= N - len; ++i) {
      auto const target = rh.hash_range(i, i+len);
      for (int j = i + len; j <= N - len; ++j) {
        if (rh.hash_range(j, j+len) == target) {
          possible = true;
        }
      }
    }
    (possible? lb : ub) = len;
  }
  cout << lb << endl;
}

int main() {
  int N;
  string S;
  cin >> N >> S;
  solve(N, S);
  return 0;
}


Measured memory_bytes: 512