#include <bits/stdc++.h>
#define loop(n) for (int ngtkana_is_genius = 0; ngtkana_is_genius < int(n); ngtkana_is_genius++)
#define rep(i, begin, end) for(int i = int(begin); i < int(end); i++)
#define all(v) v.begin(), v.end()
#define lint long long
auto cmn = [](auto& a, auto b){if (a > b) {a = b; return true;} return false;};
auto cmx = [](auto& a, auto b){if (a < b) {a = b; return true;} return false;};
void debug_impl() { std::cerr << std::endl; }
template <typename Head, typename... Tail>
void debug_impl(Head head, Tail... tail){
  std::cerr << " " << head;
  debug_impl(tail...);
}
#define debug(...)\
  std::cerr << std::boolalpha << "[" << #__VA_ARGS__ << "]:";\
  debug_impl(__VA_ARGS__);\
  std::cerr << std::noboolalpha;

class suffix_array {
    int                n;
    std::string        s;
    std::vector< int > suf;
    std::vector< int > rank;
    std::vector< int > lcp;

  public:
    suffix_array()=default;
    suffix_array(const std::string& s) :
      n(s.length()), s(s), suf(n), rank(n), lcp(n) {}

    void build() {
      suf.resize(n, 0);
      rank.resize(n, 0);
      std::iota(suf.begin(), suf.end(), 0);
      for (auto i = 0; i < n; i++)
        { rank.at(i) = s.at(i); }
      auto comb = 1;
      auto cmp = [&](auto i, auto j) {
        if (rank.at(i) < rank.at(j)) return true;
        if (rank.at(i) > rank.at(j)) return false;
        if (n <= i + comb) return true;
        if (n <= j + comb) return false;
        return rank.at(i + comb) < rank.at(j + comb);
      };
      std::vector< int > buff(n);
      for (; comb < n; comb *= 2) {
        std::sort(suf.begin(), suf.end(), cmp);
        buff.at(suf.front()) = 0;
        for (auto i = 1; i < n; i++) {
          buff.at(suf.at(i)) = buff.at(suf.at(i - 1)) + cmp(suf.at(i - 1), suf.at(i));
        }
        buff.swap(rank);
      }
      for (auto i = 0; i < n; i++)
        { rank.at(suf.at(i)) = i; }

      auto now = 0;
      for (auto i = 0; i < n; i++) {
        auto x = rank.at(i);
        if (x == n - 1) continue;
        auto y = x + 1;
        auto j = suf.at(y);
        if (0 < now) now--;
        for (; now + std::max(i, j) < n; now++) {
          if (s.at(i + now) != s.at(j + now))
            { break; }
        }
        lcp.at(x) = now;
      }
    }

    auto get_suf() const { return suf; }
    auto get_lcp() const { return lcp; }
};

template <typename T>
std::istream& operator>> (std::istream& is, std::vector<T>& v) {
  for (auto & x : v) is >> x;
  return is;
}

template <typename T>
std::ostream& operator<< (std::ostream& os, const std::vector<T>& v) {
  auto n = v.size();
  os << "{";
  for (size_t i = 0; i < n; i++)
    {os << (i > 0 ? "," : "") << v.at(i);}
  return os << "}";
}

int main() {
  std::cin.tie(0); std::cin.sync_with_stdio(false);
  int n; std::string s; std::cin >> n >> s;
  auto sa = suffix_array(s);
  sa.build();
  auto suf = sa.get_suf();
  auto lcp = sa.get_lcp();
  auto lwr = 0, upr = n + 1;
  while (1 < upr - lwr) {
    int mid = (lwr + upr) / 2;
    bool found = false;
    rep(i, 0, n) {
      auto min = suf.at(i);
      auto max = suf.at(i);
      while (i + 1 < n && mid <= lcp.at(i)) {
          i++;
          cmn(min, suf.at(i));
          cmx(max, suf.at(i));
      }
      if (mid <= max - min)
        { found = true; break; }
    }
    (found ? lwr : upr) = mid;
  }
  auto ret = lwr;
  std::cout << ret << std::endl;
  return 0;
}


Measured memory_bytes: 384