/**
 *      purpose : 
 *      author  : kyomukyomupurin
 *      created : 
**/

// input/output
#include <fstream>
#include <iostream>
#include <sstream>
// container class
#include <array>
#include <deque>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
// math, algorithm
#include <algorithm>
#include <cmath>
#include <complex>
#include <numeric>
// etc
#include <bitset>
#include <cassert>
#include <cctype>
#include <cstring>
#include <chrono>
#include <ext/random>
#include <ext/rope>
#include <functional>
#include <iomanip>
#include <random>
#include <utility>
// using-directive
using namespace std;
// alias template
using int64 = long long;
using uint64 = unsigned long long;
using vi = std::vector<int>;
using vl = std::vector<int64>;
using pii = std::pair<int, int>;
using pii = std::pair<int, int>;
using pll = std::pair<int64, int64>;
// text macro replacement
#define rep(i, n) for (int i = 0; i < (int)(n); ++i)
#define all(_) (_).begin(), (_).end()
#define rall(_) (_).rbegin(), (_).rend()
#define print(_) std::cout << (_) << '\n'
#define debug(_) std::cerr << #_ << ": " << (_) << '\n'
// variadic template
template<typename T> inline void chmin(T &a, T b) {if (a > b) a = b; return;}
template<typename T> inline void chmax(T &a, T b) {if (a < b) a = b; return;}
template<typename T> std::ostream& operator<<(std::ostream& os, const std::vector<T>& vec) {
  os << "{";
  for (size_t i = 0; i < vec.size(); ++i) {
    os << vec[i] << ",}"[i == vec.size() - 1];
  }
  return os;
}
template<typename T, typename U> std::ostream& operator<<(std::ostream& os, const std::pair<T, U>& p) {
  return os << "{" << p.first << " " << p.second << "}";
}
// constant
const int INF = (1<<30) - 1;  // 1.073741823e9
const int64 INF64 = (1LL<<62) - 1;  // 4.611686018427387903e18
const int MOD = 1000000007;

class RollingHash {
 public:
  RollingHash(const std::string& s) : s_(s) { Initialize(); }
  std::pair<unsigned int, unsigned int> GetHash(int left, int right);

 private:
  const std::string s_;
  static std::vector<unsigned int> bases_;
  static unsigned int base0_;
  static unsigned int base1_;
  std::vector<unsigned int> hash0_;
  std::vector<unsigned int> hash1_;
  std::vector<unsigned int> power0_;
  std::vector<unsigned int> power1_;
  static constexpr unsigned int mod0_ = 2020202077;
  static constexpr unsigned int mod1_ = 2020202111;
  void Initialize();
  unsigned int mul0(unsigned int a, unsigned int b);
  unsigned int mul1(unsigned int a, unsigned int b);
};

std::vector<unsigned int> RollingHash::bases_ = {
    10007, 10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099,
    10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177, 10181,
    10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271, 10273, 10289,
    10301, 10303, 10313, 10321, 10331, 10333, 10337, 10343, 10357, 10369, 10391,
    10399, 10427, 10429, 10433, 10453, 10457, 10459, 10463, 10477, 10487, 10499,
    10501, 10513, 10529, 10531, 10559, 10567, 10589, 10597, 10601, 10607, 10613,
    10627, 10631, 10639, 10651, 10657, 10663, 10667, 10687, 10691, 10709, 10711,
    10723, 10729, 10733, 10739, 10753, 10771, 10781, 10789, 10799, 10831, 10837,
    10847, 10853, 10859, 10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937,
    10939, 10949, 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057,
    11059, 11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149,
    11159, 11161, 11171, 11173, 11177, 11197};

unsigned int RollingHash::base0_ =
    bases_[std::chrono::steady_clock::now().time_since_epoch().count() & 127];

unsigned int RollingHash::base1_ =
    bases_[std::chrono::steady_clock::now().time_since_epoch().count() & 127];

// return hash pair of [left, right)
inline std::pair<unsigned int, unsigned int> RollingHash::GetHash(int left, int right) {
  unsigned int ret0 =
      hash0_[right] + mod0_ - mul0(hash0_[left], power0_[right - left]);
  if (ret0 >= mod0_) ret0 -= mod0_;
  unsigned int ret1 =
      hash1_[right] + mod1_ - mul1(hash1_[left], power1_[right - left]);
  if (ret1 >= mod1_) ret1 -= mod1_;

  return {ret0, ret1};
}

void RollingHash::Initialize() {
  int ssize = static_cast<int>(s_.size());
  //hash0_.assign(ssize + 1, 0);
  //hash1_.assign(ssize + 1, 0);
  //power0_.assign(ssize + 1, 0);
  //power1_.assign(ssize + 1, 0);
  //power0_[0] = 1;
  //power1_[0] = 1;
  hash0_.emplace_back(0);
  hash1_.emplace_back(0);
  power0_.emplace_back(1);
  power1_.emplace_back(1);
  for (int i = 0; i < ssize; ++i) {
    //hash0_[i + 1] = mul0(hash0_[i], base0_) + s_[i];
    //hash1_[i + 1] = mul1(hash1_[i], base1_) + s_[i];
    //power0_[i + 1] = mul0(power0_[i], base0_);
    //power1_[i + 1] = mul1(power1_[i], base1_);
    hash0_.emplace_back(mul0(hash0_.back(), base0_) + s_[i]);
    hash1_.emplace_back(mul1(hash1_.back(), base1_) + s_[i]);
    power0_.emplace_back(mul0(power0_.back(), base0_));
    power1_.emplace_back(mul1(power1_.back(), base1_));
    if (hash0_.back() >= mod0_) hash0_.back() -= mod0_;
    if (hash1_.back() >= mod1_) hash1_.back() -= mod1_;
  }
}

inline unsigned int RollingHash::mul0(unsigned int a, unsigned int b) {
  unsigned long long x =
      static_cast<unsigned long long>(a) * static_cast<unsigned long long>(b);
  unsigned int xh = static_cast<unsigned int>(x >> 32),
               xl = static_cast<unsigned int>(x), d, m;
  asm("divl %4; \n\t" : "=a"(d), "=d"(m) : "d"(xh), "a"(xl), "r"(mod0_));
  return m;
}

inline unsigned int RollingHash::mul1(unsigned int a, unsigned int b) {
  unsigned long long x =
      static_cast<unsigned long long>(a) * static_cast<unsigned long long>(b);
  unsigned int xh = static_cast<unsigned int>(x >> 32),
               xl = static_cast<unsigned int>(x), d, m;
  asm("divl %4; \n\t" : "=a"(d), "=d"(m) : "d"(xh), "a"(xl), "r"(mod1_));
  return m;
}

int main(){
  std::ios_base::sync_with_stdio(false);
  std::cin.tie(nullptr);

  int n; cin >> n;
  string s; cin >> s;

  RollingHash rh(s);

  auto ok = [&](int x) -> bool{
    for (int i = 0; i + 2 * x <= n; ++i) {
      for (int j = i + x; j + x <= n; ++j) {
        if (rh.GetHash(i, i + x) == rh.GetHash(j, j + x)) {
          return true;
        }
      }
    }
    return false;
  };
 
  int low = 0, high = n / 2 + 1;
 
  while (high - low > 1) {
    int mid = (low + high) >> 1;
    if (ok(mid)) {
      low = mid;
    } else {
      high = mid;
    }
  }

  cout << low << '\n';

  return 0;
}


Measured memory_bytes: 384