#include <bits/stdc++.h>
#define LLI long long int
#define FOR(v, a, b) for(LLI v = (a); v < (b); ++v)
#define FORE(v, a, b) for(LLI v = (a); v <= (b); ++v)
#define REP(v, n) FOR(v, 0, n)
#define REPE(v, n) FORE(v, 0, n)
#define REV(v, a, b) for(LLI v = (a); v >= (b); --v)
#define ALL(x) (x).begin(), (x).end()
#define RALL(x) (x).rbegin(), (x).rend()
#define ITR(it, c) for(auto it = (c).begin(); it != (c).end(); ++it)
#define RITR(it, c) for(auto it = (c).rbegin(); it != (c).rend(); ++it)
#define EXIST(c,x) ((c).find(x) != (c).end())
#define fst first
#define snd second
#define popcount __builtin_popcount
#define UNIQ(v) (v).erase(unique(ALL(v)), (v).end())
#define bit(i) (1LL<<(i))

#ifdef DEBUG
#include <misc/C++/Debug.cpp>
#else
#define dump(...) ((void)0)
#endif

#define gcd __gcd

using namespace std;
template <class T> constexpr T lcm(T m, T n){return m/gcd(m,n)*n;}

template <typename I> void join(ostream &ost, I s, I t, string d=" "){for(auto i=s; i!=t; ++i){if(i!=s)ost<<d; ost<<*i;}ost<<endl;}
template <typename T> istream& operator>>(istream &is, vector<T> &v){for(auto &a : v) is >> a; return is;}

template <typename T, typename U> bool chmin(T &a, const U &b){return (a>b ? a=b, true : false);}
template <typename T, typename U> bool chmax(T &a, const U &b){return (a<b ? a=b, true : false);}
template <typename T, size_t N, typename U> void fill_array(T (&a)[N], const U &v){fill((U*)a, (U*)(a+N), v);}

struct Init{
  Init(){
    cin.tie(0);
    ios::sync_with_stdio(false);
  }
}init;

template <typename T> class SparseTableMin{
  vector<vector<T>> a;
  vector<int> log_table;
public:
  SparseTableMin(){}
  SparseTableMin(const vector<T> &v){
    int n = v.size();
    int logn = 0;
    while((1<<logn) <= n) ++logn;
    
    a = vector<vector<T>>(n, vector<T>(logn));
    REP(i,n) a[i][0] = v[i];
    FOR(j,1,logn) REP(i,n) a[i][j] = min(a[i][j-1], a[min<int>(n-1, i+(1<<(j-1)))][j-1]);

    log_table = vector<int>(n+1);
    FOR(i,2,n+1) log_table[i] = log_table[i>>1] + 1;
  }
  
  T get(int s, int t) const { // [s,t)
    int k = log_table[t-s];
    return min(a[s][k], a[t-(1<<k)][k]);
  }
};


template <typename T> class SuffixArray{
public:
  const T str;
  int n;
  vector<int> sa, rank, lcp;

  SparseTableMin<int> sp_lcp;
  
  SuffixArray(const T &s): str(s), n(s.size()), sa(n), rank(n), lcp(n-1){
    vector<int> temp(n);
    REP(i,n) temp[i] = s[i];

    for(int l=1; l<n; l*=2){
      map<pair<int,int>, int> m2;
      REP(i,n) m2[make_pair(temp[i], i+l>=n?-1:temp[i+l])] = 0;
      int i=0;
      ITR(it,m2) it->second = i++;
      REP(i,n) temp[i] = m2[make_pair(temp[i], i+l>=n?-1:temp[i+l])];
    }
    
    REP(i,n) sa[temp[i]] = i;

    REP(i,n) rank[sa[i]] = i;

    /*
      calculate LCP array. O(n)
     */
    int h = 0;
    REP(i,n){
      if(rank[i] == 0) continue;
      int j = sa[rank[i]-1];

      if(h) --h;
      while(j+h < n and i+h < n){
        if(s[j+h] != s[i+h]) break;
        ++h;
      }

      lcp[rank[i]-1] = h;
    }

    sp_lcp = SparseTableMin<int>(lcp);
  }

  int get_lcp(int i, int j) const {
    if(i == j) return n - i;
    
    i = rank[i];
    j = rank[j];

    if(i > j) swap(i,j);

    return sp_lcp.get(i,j);
  }

  int operator[](int i){return sa[i];}

  bool starts_with(const T &s, int k){
    if(s.size() <= str.size()-k){
      REP(i,(int)s.size()){
        if(s[i] != str[k+i]) return false;
      }
      return true;
    }
    return false;
  }

  int lower_bound(const T &s){
    auto check =
      [&](int x){
        REP(i,(int)s.size()){
          if(sa[x]+i >= (int)str.size()) return false;
          if(s[i] < str[sa[x]+i]) return true;
          if(s[i] > str[sa[x]+i]) return false;
        }
        return true;
      };

    int lb=-1, ub=sa.size(), mid;
    while(abs(lb-ub)>1){
      mid = (lb+ub)/2;

      if(check(mid)){
        ub = mid;
      }else{
        lb = mid;
      }
    }
    
    return ub;
  }

  int upper_bound(const T &s){
    T t(s);

    t.back()++;
    int ret = lower_bound(t);
    t.back()--;
    return ret;
  }
};

int main(){
  int N;
  string s;
  while(cin >> N >> s){
    int ans = 0;

    dump(s);

    SuffixArray<string> sa(s);

    dump(sa.sa, sa.rank, sa.lcp);

    REP(i,N){
      dump(s.substr(sa[i]));
    }
    
    REP(i,N){
      REP(j,i){
        int t = sa.get_lcp(i,j);
        dump(make_tuple(i,j,t));
        if(j+t <= i) chmax(ans, t);
      }
    }

    cout << ans << endl;
  }

  return 0;
}


Measured memory_bytes: 896