#include <bits/stdc++.h>
using namespace std;
const int Maxn = 2e5 + 10, S = 26;
int n, a[Maxn];
char s[Maxn];
struct SEG {
  int top;
  struct node {
    int c[2], siz;
  } T[Maxn * 100];
  int insert(int l, int r, int pos) {
    int u = ++top;
    T[u].siz = 1;
    if (l == r) return u;
    int mid = l + r >> 1;
    if (pos <= mid)
      T[u].c[0] = insert(l, mid, pos);
    else
      T[u].c[1] = insert(mid + 1, r, pos);
    return u;
  }
  int merge(int u, int v) {
    if (!u || !v) return u + v;
    int x = ++top;
    T[x].siz = T[u].siz + T[v].siz;
    T[x].c[0] = merge(T[u].c[0], T[v].c[0]);
    T[x].c[1] = merge(T[u].c[1], T[v].c[1]);
    return x;
  }
  int query(int u, int l, int r, int li, int ri) {
    if (!u || !T[u].siz || l > r || l > ri || li > r || li > ri) return 0;
    if (l == li && r == ri) return 1;
    int mid = l + r >> 1;
    return query(T[u].c[0], l, mid, li, min(mid, ri)) |
           query(T[u].c[1], mid + 1, r, max(mid + 1, li), ri);
  }
} Tree;
struct SAM {
  int top, suf[Maxn];
  struct node {
    int c[S], mx, fail, pos, rt, tf, rk;
  } T[Maxn << 1];
  int insert(int p, int c) {
    int u = ++top;
    T[u].mx = T[p].mx + 1;
    for (; p && !T[p].c[c]; p = T[p].fail) T[p].c[c] = u;
    if (!p)
      T[u].fail = 1;
    else {
      int v = T[p].c[c];
      if (T[p].mx + 1 == T[v].mx)
        T[u].fail = v;
      else {
        int now = ++top;
        memcpy(T[now].c, T[v].c, sizeof(int) * S);
        T[now].mx = T[p].mx + 1;
        T[now].fail = T[v].fail, T[v].fail = T[u].fail = now;
        for (; T[p].c[c] == v; p = T[p].fail) T[p].c[c] = now;
      }
    }
    return u;
  }
  void build() {
    suf[0] = top = 1;
    for (int i = 1; i <= n; i++) {
      suf[i] = insert(suf[i - 1], a[i]);
      T[suf[i]].pos = i;
      T[suf[i]].rt = Tree.insert(1, n, i);
    }
    solve();
  }
  void solve() {
    static int a[Maxn << 1], c[Maxn];
    for (int i = 1; i <= top; i++) c[T[i].mx]++;
    for (int i = 1; i <= n; i++) c[i] += c[i - 1];
    for (int i = 1; i <= top; i++) a[c[T[i].mx]--] = i;
    for (int i = top; i > 1; i--) {
      int u = a[i];
      T[T[u].fail].pos = T[u].pos;
      T[T[u].fail].rt = Tree.merge(T[T[u].fail].rt, T[u].rt);
    }
    T[1].tf = 1;
    for (int i = 2; i <= top; i++) {
      int u = a[i], v = T[T[u].fail].tf;
      if (v == 1)
        T[u].rk = 1, T[u].tf = u;
      else {
        if (Tree.query(T[v].rt, 1, n, T[u].pos - T[u].mx + T[v].mx,
                       T[u].pos - 1))
          T[u].rk = T[v].rk + 1, T[u].tf = u;
        else
          T[u].rk = T[v].rk, T[u].tf = v;
      }
    }
    int ans = 0;
    for (int i = 2; i <= top; i++) ans = max(ans, T[i].rk);
    printf("%d\n", ans);
  }
} Sam;
int main() {
  scanf("%d", &n);
  scanf("%s", s + 1);
  for (int i = 1; i <= n; i++) a[i] = s[i] - 'a';
  Sam.build();
  return 0;
}
