#include <iostream>
#include <vector>
#include <algorithm>
#include <cassert>
using namespace std;
const int INF = 1e9;

//構築 : O(|S|log|S|)
struct SuffixArray {
private:
  string s;
  size_t size;
  void constructLCPArray(){
    for(size_t i = 0; i < size; ++i){
      rank[SA[i]] = i;
    }
    int l = 0;
    for(size_t i = 0; i < size; ++i){
      int idx = rank[i];
      int p1 = SA[idx];
      if(idx+1 == size){
        l = 0;
        LCP[idx] = l;
        continue;
      }
      int p2 = SA[idx+1];
      while(max(p1,p2)+l < size and s[p1+l] == s[p2+l]) ++l;
      LCP[idx] = l;
      --l;
      l = max(l,0);
    }
  }
public:
  vector<int> SA, LCP, rank;
  SuffixArray(string s_) : s(s_), size(s_.length()+1), SA(size+1), LCP(size+1), rank(size+1) {
    s += "$";
  }
  void construct(){
    vector<int> cnt(max((size_t)256,size),0);
    for(auto e : s){
      ++cnt[e];
    }
    for(size_t i = 1; i < cnt.size(); ++i)
      cnt[i] += cnt[i-1];
    vector<int> p(size);
    for(size_t i = 0; i < size; ++i){
      p[--cnt[s[i]]] = i;
    }
    vector<int> c(size);
    c[p[0]] = 0;
    for(size_t i = 1; i < size; ++i){
      c[p[i]] = c[p[i-1]] + (s[p[i]] != s[p[i-1]]);
    }

    for(size_t k = 1; k < size; k *= 2){
      vector<int> p_(size), c_(size);
      for(size_t j = 0; j < size; ++j){
        p_[j] = p[j] - k;
        if(p_[j] < 0) p_[j] += size;
      }
      fill(cnt.begin(),cnt.end(),0);

      int max_e = 0;
      for(auto e : c){
        ++cnt[e], max_e = max(max_e,e);
      }
      for(int i = 0; i < max_e; ++i){
        cnt[i+1] += cnt[i];
      }
      
      for(size_t i = size-1; i < size; --i){
        p[--cnt[c[p_[i]]]] = p_[i];
      }
      
      c_[p[0]] = 0;
      int classes = 1;
      for(size_t i = 1; i < size; ++i){
        if((c[p[i]] != c[p[i-1]]) or (c[(p[i]+k)%size] != c[(p[i-1]+k)%size]))
          ++classes;
        c_[p[i]] = classes-1;
      }
      swap(c,c_);
    }

    SA = p;
    constructLCPArray();
  }
};

struct RMinQ{
private:
  int n;
  vector<int> data;
public:
  RMinQ(const vector<int> &V){
    int sz = V.size();
    n = 1;
    while(n < sz) n *= 2;
    data.resize(2*n-1,INF);
    for(int i = 0; i < sz; ++i) data[n-1+i] = V[i];
    for(int i = n-2; i >= 0; --i) data[i] = min(data[2*i+1],data[2*i+2]);
  }
  void update(int idx, int val){//0-indexed
    idx += n-1;
    data[idx] = val;
    while(idx > 0){
      idx = (idx-1)/2;
      data[idx] = min(data[2*idx+1],data[2*idx+2]);
    }
  }
  int get_min(int l, int r){//[l,r)の区間の最小値
    int ret = INF;
    l += n-1, r += n-1;
    while(l < r){
      if((l&1) == 0){
        ret = min(ret,data[l]);
      }
      if((r&1) == 0){
        ret = min(ret,data[r-1]);
      }
      l /= 2;
      r = (r-1)/2;
    }
    return ret;
  }
};

int main(){
  int N;
  string s;
  cin >> N >> s;
  int ans = 0;
  SuffixArray sa(s);
  sa.construct();
  RMinQ rmq(sa.LCP);
  for(int i = 0; i < N; ++i){
    if(N-i < ans) break;
    int M = N-i;
    for(int j = 0; j < M; ++j){
      if(M-j < ans) break;
      int l = sa.rank[i];
      int r = sa.rank[i+j];
      if(r < l) swap(l,r);
      int p = rmq.get_min(l,r);
      ans = max(ans,min({p,j,M-j}));
    }
  }
  cout << ans << endl;
}


Measured memory_bytes: 512