#include <bits/stdc++.h>


template <typename T>
struct PrintVector{
  const std::vector<T> value;
  const std::string open, close, delim;

  PrintVector(const std::vector<T> &value, const std::string &delim, const std::string &open, const std::string &close):
    value(value), open(open), close(close), delim(delim){}

  std::ostream& operator()(std::ostream &s) const {
    s << open;
    
    const auto cbegin = value.cbegin();
    const auto cend = value.cend();

    for(auto it = cbegin; it != cend; ++it){
      if(it != cbegin) s << delim;
      s << *it;
    }

    s << close;
    return s;
  }

  friend std::ostream& operator<<(std::ostream &s, const PrintVector<T> &manip){
    return manip(s);
  }
};

template <typename T>
auto print_vector(const std::vector<T> &value, const std::string delim, const std::string &open = "", const std::string &close = ""){
  return PrintVector<T>(value, delim, open, close);
}



template <typename T> class SuffixArray{
public:
  const T str;
  const int n;
  std::vector<int> suffix_array;
  
  SuffixArray(const T &s): str(s), n(s.size()), suffix_array(n){
    std::vector<int> temp(n);
    for(int i = 0; i < n; ++i) temp[i] = s[i];

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

      int i=0;

      for(auto it = m2.begin(); it != m2.end(); ++it) it->second = i++;

      for(int i = 0; i < n; ++i) temp[i] = m2[std::make_pair(temp[i], i+l >= n ? -1 : temp[i+l])];
    }
    
    for(int i = 0; i < n; ++i) suffix_array[temp[i]] = i;
  }

  int operator[](int i) const {return suffix_array[i];}

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

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

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

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

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

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



template <typename T>
struct LCPArray{
  const int n;
  std::vector<int> rank, lcp_array;

  LCPArray(const SuffixArray<T> &sa): n(sa.n), rank(n), lcp_array(n-1){
    for(int i = 0; i < n; ++i) rank[sa[i]] = i;

    int h = 0;
    for(int i = 0; i < n; ++i){
      if(rank[i] == 0) continue;
      int j = sa[rank[i]-1];

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

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


template <typename Semigroup>
class SparseTable{
  using value_type = typename Semigroup::value_type;
  
  std::vector<std::vector<value_type>> a;
  std::vector<int> log_table;
  
public:
  SparseTable(const std::vector<value_type> &v){
    int n = v.size();
    int logn = 0;
    while((1 << logn) <= n) ++logn;
    
    a.assign(n, std::vector<value_type>(logn));
    for(int i = 0; i < n; ++i) a[i][0] = v[i];
    for(int j = 1; j < logn; ++j){
      for(int i = 0; i < n; ++i){
        a[i][j] = Semigroup::op(a[i][j-1], a[std::min<int>(n-1, i+(1<<(j-1)))][j-1]);
      }
    }

    log_table.assign(n+1, 0);
    for(int i = 2; i < n+1; ++i) log_table[i] = log_table[i>>1] + 1;
  }
  
  inline value_type get(int s, int t) const { // [s,t)
    int k = log_table[t-s];
    return Semigroup::op(a[s][k], a[t-(1<<k)][k]);
  }

  inline value_type get(std::vector<std::pair<int,int>> st) const {
    value_type ret;
    bool t = true;

    for(const auto &p : st){
      if(p.first < p.second){
        if(t){
          ret = get(p.first, p.second);
          t = false;
        }else{
          ret = Semigroup::op(ret, get(p.first, p.second));
        }
      }
    }

    return ret;
  }
};


template <typename T>
struct MinMonoid{
  using value_type = T;
  constexpr inline static value_type id(){return std::numeric_limits<T>::max();}
  constexpr inline static value_type op(const value_type &a, const value_type &b){return std::min(a, b);}
};




int main(){
  int N; std::cin >> N;
  std::string S; std::cin >> S;

  int ans = 0;

  SuffixArray<std::string> sa(S);
  LCPArray<std::string> lcp(sa);

  SparseTable<MinMonoid<int>> sp(lcp.lcp_array);

  for(int i = 0; i < N; ++i){
    for(int j = 0; j < i; ++j){
      int l = lcp.rank[i], r = lcp.rank[j];
      if(l > r) std::swap(l, r);
      int t = sp.get(l, r);
      if(j + t <= i) ans = std::max(ans, t);
    }
  }

  std::cout << ans << std::endl;

  return 0;
}


Measured memory_bytes: 768