#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
using namespace std;
template<typename T> T rng(T left, T right) {
  static mt19937 mt(chrono::steady_clock::now().time_since_epoch().count());
  return uniform_int_distribution<T>(left, right - 1)(mt);
}

class RollingHash {
private:
  static constexpr int mod_1 = 1000000007;
  static constexpr int mod_2 = 1000000009;
  static long long base_1;
  static long long base_2;
  static vector<long long> pow_1;
  static vector<long long> pow_2;
  vector<long long> hash_1;
  vector<long long> hash_2;
  const int siz;
  template<typename Itr> void build(Itr first, Itr last) {
    hash_1.resize(siz + 1);
    hash_2.resize(siz + 1);
    for (int i = 0; i < siz; ++i, ++first) {
      hash_1[i + 1] = (hash_1[i] * base_1 % mod_1 + *first) % mod_1;
      hash_2[i + 1] = (hash_2[i] * base_2 % mod_2 + *first) % mod_2;
    }
    while ((int) pow_1.size() <= siz) {
      pow_1.push_back(pow_1.back() * base_1 % mod_1);
      pow_2.push_back(pow_2.back() * base_2 % mod_2);
    }
  }
  long long get_1(int left, int right) { return (hash_1[right] + (mod_1 - hash_1[left]) * pow_1[right - left] % mod_1) % mod_1; }
  long long get_2(int left, int right) { return (hash_2[right] + (mod_2 - hash_2[left]) * pow_2[right - left] % mod_2) % mod_2; }
public:
  template<typename Itr> RollingHash(Itr first, Itr last) : siz(distance(first, last)) { build(first, last); }
  template<typename T> RollingHash(const T in) : siz(distance(in.begin(), in.end())) { build(in.begin(), in.end()); }

  pair<long long, long long> get(int left, int right) { return make_pair(get_1(left, right), get_2(left, right)); }
  bool is_same(int left_1, int right_1, int left_2, int right_2) { return get(left_1, right_1) == get(left_1, right_2); }
  bool is_same(int left_1, int left_2, int len) { return is_same(left_1, left_1 + len, left_2, left_2 + len); }
  bool is_same(int left, int right, pair<long long, long long> hash) { return get(left, right) == hash; }

  pair<long long, long long> const show_base() { return make_pair(base_1, base_2); }
  void show_hashes() {
    for (int i = 0; i < (int) hash_1.size(); i++) {
      cerr << "i = " << i << " " << hash_1[i] << " " << hash_2[i] << endl;
    }
  }
  void show_pows() {
    for (int i = 0; i < (int) pow_1.size(); i++) {
      cerr << "i = " << i << " " << pow_1[i] << " " << pow_2[i] << endl;
    }
  }
};

long long RollingHash::base_1 = rng<unsigned>(1, RollingHash::mod_1);
long long RollingHash::base_2 = rng<unsigned>(1, RollingHash::mod_2);
vector<long long> RollingHash::pow_1{1};
vector<long long> RollingHash::pow_2{1};
using RH = RollingHash;
int n;
string s;
bool is_cool (int len, RH& rh) {
  //cerr << "len = " << len << endl;
  for (int i = 0; i < n; i++) {
    for (int j = i + len; j + len <= n; j++) {
      auto a = rh.get(i, i + len);
      auto b = rh.get(j, j + len);
      if (a == b) return true;
    }
  }
  return false;
}
signed main() { 
  ios::sync_with_stdio(false); cin.tie(0);
  //e
  cin >> n;
  cin >> s;
  RH rh(s);
  int lo = 0, hi = n / 2 + 1;
  while (hi - lo > 1) {
    int mid = (hi + lo) / 2;
    //cerr << mid << " " << is_cool(mid, rh) << endl;
    if (is_cool(mid, rh)) lo = mid;
    else hi = mid;
    //if (lo == mid) cerr << "ok" << endl;
    //else cerr << "no" << endl;
    //cerr << lo << " " << hi << endl;
  }
  if (is_cool(hi, rh)) {
    cout << hi << '\n';
  } else {
    cout << lo << '\n';
  }
  return 0;
}

Measured memory_bytes: 524