#include <bits/stdc++.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/irange.hpp>
#include <boost/multiprecision/cpp_int.hpp>
using namespace std;
using boost::irange;
using boost::algorithm::join;

typedef long long ll;
typedef unsigned long long ull;
//typedef long double ld;

typedef map<int, int> mii;
typedef pair<int, int> pii;
typedef pair<double,double> pdd;
typedef set<int> si;
typedef set<ll> sll;
typedef vector<int> vi;
typedef vector<pair<int, int > > vii;
typedef vector<ll> vll;
typedef vector<double> vd;
typedef vector<vi> vvi;
typedef vector<vii> vvii;
typedef vector<vll> vvll;

constexpr ll MOD = 1e9+7;
template<typename T>constexpr auto inf=numeric_limits<T>::max()/4;
constexpr int INF=inf<int>;
constexpr ll LINF=inf<ll>;

#define Sort(x) sort(begin(x),end(x))
#define Reverse(x) reverse(begin(x),end(x))
#define ABS(a,b) ((a)<(b)?(b)-(a):(a)-(b))
#define nbit(n) (1ull<<(n))
#define _ol3(_1,_2,_3,name,...)name
#define _rep(i,n)_repi(i,0,n)
#define _repi(i,a,b)for(int i=a,i##_l=(b);i<i##_l;++i)
#define REP(...) _ol3(__VA_ARGS__,_repi,_rep,)(__VA_ARGS__)
#define REPR(i,n) for(int i=n-1;i>=0;--i)
#define REPA(i,v) REP(i,(v).size())

// sort(begin(x), end(x), greater<ll>());
// lower_bound(a.begin(), a.end(), val): a[i] ≥ valである最左の位置
// upper_bound(a.begin(), a.end(), val): a[i] > valである最左の位置

// Returns minimum of a and b.
// If a is less b, a is set to b.
template<typename T>
T chmin(T& a, T b) {
  if (a > b) {
    a = b;
  }
  return a;
}

// Returns maximum of a and b.
// If a is less b, a is set to b.
template<typename T>
T chmax(T& a, T b) {
  if (a < b) {
    a = b;
  }
  return a;
}

template <typename T>
void dumpContents(const T& v, const string& msg)
{
  cerr << "### " << msg << " ###\n";
  for (const auto& x : v) {
    cerr << x << " ";
  }
  cerr << endl;
}

struct before_main_function {
  before_main_function() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout<<setprecision(20)<<fixed;
    #define endl "\n"
  }
} before_main_function;

// ========== end of template ==========
int N;
string S;

bool check(int len) {
  unordered_map<string, si> m;

  for (auto i : irange(0,N-len+1)) {
    const string& s = S.substr(i, len);
    m[s].insert(i);
  }

  for (auto kv : m) {
    if (kv.second.size() < 2) continue;
    auto it1 = kv.second.begin();
    auto it2 = kv.second.end();
    it2--;
    if (*it1+len > *it2) continue;

//cerr << "len " << len << ": true" << endl;
    return true;
  }

//cerr << "len " << len << ": false" << endl;

  return false;
}

// [s,e)
// s should be true
int binary_search(int s, int e) {
//cerr << "["<<s<<","<<e<<")"<<endl;
  if (e<=s) return 0;

  int m = (s+e)/2;

  bool mb = check(m);
  if (s==m) {
    assert(mb);
    return s;
  }

  if (mb) {
    return binary_search(m, e);
  }

  return binary_search(s, m);
}

vi ZAlgorithm(const string &s) {
  vi prefix(s.size());

  for (int i=1, j=0; i<int(s.size()); i++) {
    if (i + prefix[i-j] < j + prefix[j]) {
      prefix[i] = prefix[i-j];
    } else {
      int k = max(0, j + prefix[j] - i);
      while (i+k < int(s.size()) && s[k] == s[i+k]) k++;
      prefix[i] = k;
      j = i;
    }
  }
  prefix[0] = s.size();

  return prefix;
}

int main(int argc, char** argv) {

  cin >> N >> S;

  int ret = 0;

  REP(i, N) {
    string ss = S.substr(i);
    vi zs = ZAlgorithm(ss);
    REP(j, zs.size()) {
      chmax(ret, min({j, zs[j]}));
    }
    //cerr << ss << ", i: " << i << ": " << ret << endl;
  }

  cout << ret << endl;

  return 0;
}


Measured memory_bytes: 256