#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,m,n) for(int i=(m);i<(n);++i)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
using ll = long long;
template <typename T> using posteriority_queue = priority_queue<T, vector<T>, greater<T> >;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3fLL;
const double EPS = 1e-8;
const int MOD = 1000000007;
// const int MOD = 998244353;
const int dy[] = {1, 0, -1, 0}, dx[] = {0, -1, 0, 1};
// const int dy[] = {1, 1, 0, -1, -1, -1, 0, 1}, dx[] = {0, -1, -1, -1, 0, 1, 1, 1};
template <typename T, typename U> inline bool chmax(T &a, U b) { return a < b ? (a = b, true) : false; }
template <typename T, typename U> inline bool chmin(T &a, U b) { return a > b ? (a = b, true) : false; }
template <typename T> void unique(vector<T> &a) { a.erase(unique(ALL(a)), a.end()); }
struct IOSetup {
  IOSetup() {
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
    cout << fixed << setprecision(20);
  }
} iosetup;

template <typename MeetSemilattice>
struct SparseTable {
  using Fn = function<MeetSemilattice(MeetSemilattice, MeetSemilattice)>;

  SparseTable() {}

  SparseTable(const vector<MeetSemilattice> &a, Fn fn, MeetSemilattice UNITY = 0) { init(a, fn); }

  void init(const vector<MeetSemilattice> &a, Fn fn_) {
    fn = fn_;
    int n = a.size(), table_h = 0;
    lg.assign(n + 1, 0);
    FOR(i, 2, n + 1) lg[i] = lg[i >> 1] + 1;
    while ((1 << table_h) <= n) ++table_h;
    dat.assign(table_h, vector<MeetSemilattice>(n));
    REP(j, n) dat[0][j] = a[j];
    FOR(i, 1, table_h) for (int j = 0; j + (1 << i) <= n; ++j) {
      dat[i][j] = fn(dat[i - 1][j], dat[i - 1][j + (1 << (i - 1))]);
    }
  }

  MeetSemilattice query(int left, int right) {
    assert(left < right);
    int h = lg[right - left];
    return fn(dat[h][left], dat[h][right - (1 << h)]);
  }

private:
  Fn fn;
  vector<int> lg;
  vector<vector<MeetSemilattice> > dat;
};

template <typename T = string>
struct SuffixArray {
  vector<int> sa, rank, lcp_array;

  SuffixArray(const T &s) : str(s) {
    int n = str.size();
    str.push_back(' ');
    sa.resize(n + 1);
    iota(ALL(sa), 0);
    sort(ALL(sa), [&](int a, int b) { return s[a] == s[b] ? a > b : s[a] < s[b]; });
    rank.resize(n + 1);
    REP(i, n + 1) rank[i] = s[i];
    vector<int> tmp_rank(n + 1), tmp_sa(n + 1), cnt(n + 1);
    for (int len = 1; len <= n; len <<= 1) {
      REP(i, n + 1) {
        if (i > 0 && rank[sa[i - 1]] == rank[sa[i]] && sa[i - 1] + len <= n && rank[sa[i - 1] + (len >> 1)] == rank[sa[i] + (len >> 1)]) {
          tmp_rank[sa[i]] = tmp_rank[sa[i - 1]];
        } else {
          tmp_rank[sa[i]] = i;
        }
      }
      copy(ALL(sa), tmp_sa.begin());
      iota(ALL(cnt), 0);
      REP(i, n + 1) {
        int idx = tmp_sa[i] - len;
        if (idx >= 0) sa[cnt[tmp_rank[idx]]++] = idx;
      }
      rank.swap(tmp_rank);
    }
    REP(i, n + 1) rank[sa[i]] = i;

    lcp_array.resize(n);
    int common = 0;
    REP(i, n) {
      int j = sa[rank[i] - 1];
      if (common > 0) --common;
      for (; i + common < n && j + common < n; ++common) {
        if (str[i + common] != str[j + common]) break;
      }
      lcp_array[rank[i] - 1] = common;
    }
    st.init(lcp_array, [](int a, int b) { return min(a, b); });
  }

  vector<int> match(T &t) {
    int lb = lower_bound(t), ub = upper_bound(t);
    vector<int> res(ub - lb);
    copy(sa.begin() + lb, sa.begin() + ub, res.begin());
    sort(ALL(res));
    return res;
  }

  int lcp(int i, int j) {
    i = rank[i];
    j = rank[j];
    if (i > j) swap(i, j);
    return st.query(i, j);
  }

private:
  T str;
  SparseTable<int> st;

  bool cmp(const string &t, int str_idx = 0, int t_idx = 0) {
    int str_sz = str.size(), t_sz = t.size();
    for (; str_idx < str_sz && t_idx < t_sz; ++str_idx, ++t_idx) {
      if (str[str_idx] < t[t_idx]) return 1;
      if (str[str_idx] > t[t_idx]) return 0;
    }
    return str_idx == str_sz && t_idx < t_sz;
  }

  int lower_bound(const T &t) {
    int lb = 0, ub = str.size();
    while (ub - lb > 1) {
      int mid = (lb + ub) >> 1;
      (cmp(t, sa[mid], 0) ? lb : ub) = mid;
    }
    return ub;
  }

  int upper_bound(T &t) {
    ++t.back();
    int res = lower_bound(t);
    --t.back();
    return res;
  }
};

int main() {
  int n; cin >> n;
  string s; cin >> s;
  SuffixArray<> suffix(s);
  int ans = 0;
  REP(i, n) FOR(j, i + 1, n) chmax(ans, min(suffix.lcp(i, j), j - i));
  cout << ans << '\n';
  return 0;
}


Measured memory_bytes: 640