local mmi, mma = math.min, math.max
local TranSufA = {}
TranSufA.makeSufA = function(self, str)
  self.sufa = {}
  local n = #str
  local idx, tbl1, tbl2 = self.sufa, {}, {}
  for i = 1, n do
    idx[i] = i
    tbl1[i] = str:sub(i, i):byte() - 95 -- "a" = 97
    tbl2[i] = 0
  end
  idx[n + 1], tbl1[n + 1], tbl2[n + 1] = n + 1, 1, 0
  n = n + 1 -- add empty to last
  table.sort(idx, function(a, b) return tbl1[a] < tbl1[b] end)
  local step, stepflag = 1, true
  while step < n do
    local v = stepflag and tbl1 or tbl2
    local vd = stepflag and tbl2 or tbl1
    stepflag = not stepflag
    local stepdst = {}
    for i = 1, n do
      local dst = i + step
      if n < dst then dst = dst - n end
      stepdst[i] = dst
    end
    local left, major = 1, v[idx[1]]
    local minor_min = v[stepdst[idx[1]]]
    local minor_max = minor_min
    local cur = 0
    for i = 1, n do
      local minor = v[stepdst[idx[i]]]
      minor_min, minor_max = mmi(minor_min, minor), mma(minor_max, minor)
      local isend = i == n or major ~= v[idx[i + 1]]
      if isend then
        local right = i
        if left == right then
          cur = cur + 1
          vd[idx[left]] = cur
        elseif minor_min ~= minor_max then
          local t = {}
          for j = left, right do
            t[j - left + 1] = idx[j]
          end
          table.sort(t, function(x, y) return v[stepdst[x]] < v[stepdst[y]] end)
          local v_prv = 0
          for j = left, right do
            idx[j] = t[j - left + 1]
            local v_nxt = v[stepdst[idx[j]]]
            if j == left or v_nxt ~= v_prv then
              cur = cur + 1
            end
            vd[idx[j]] = cur
            v_prv = v_nxt
          end
        else
          cur = cur + 1
          for j = left, right do
            vd[idx[j]] = cur
          end
        end
        if i < n then
          left, major = i + 1, v[idx[i + 1]]
          minor_min = v[stepdst[idx[i + 1]]]
          minor_max = minor_min
        end
      end
    end
    step = step * 2
  end
  -- remove empty from first (O(N))
  table.remove(self.sufa, 1)
  n = n - 1
  self.sufa_inv = {}
  for i = 1, n do self.sufa_inv[i] = 0 end
  for i = 1, n do
    self.sufa_inv[self.sufa[i]] = i
  end
end
TranSufA.makeLCPA = function(self)
  assert(self.sufa)
  local n = #self.sufa
  self.lcpa = {}
  local str, sufa, lcpa = self.str, self.sufa, self.lcpa
  for i = 1, n - 1 do lcpa[i] = 0 end
  local spos = 0
  for i = 1, n do
    local lcppos = self.sufa_inv[i]
    if lcppos < n then
      local len = spos
      local p1, p2 = sufa[lcppos], sufa[lcppos + 1]
      p1, p2 = p1 + spos, p2 + spos
      while p1 <= n and p2 <= n do
        if str:sub(p1, p1) == str:sub(p2, p2) then
          len = len + 1
          p1, p2 = p1 + 1, p2 + 1
        else break
        end
      end
      lcpa[lcppos] = len
      spos = mma(0, len - 1)
    end
  end
end

TranSufA.create = function(self, str)
  self.str = str
  self:makeSufA(str)
end

TranSufA.new = function(str)
  local obj = {}
  setmetatable(obj, {__index = TranSufA})
  obj:create(str)
  return obj
end

-- sample
local n = io.read("*n", "*l")
local s = io.read()
local sa = TranSufA.new(s)
sa:makeLCPA()
-- print(table.concat(sa.sufa, " "))
-- print(table.concat(sa.sufa_inv, " "))
-- print(table.concat(sa.lcpa, " "))

local sufa, sufa_inv, lcpa = sa.sufa, sa.sufa_inv, sa.lcpa
local ret = 0
for i = 1, n - 1 do
  local len = lcpa[i]
  local cand = len
  local p1, p2 = sufa[i], sufa[i + 1]
  if p1 < p2 then
    ret = mma(ret, mmi(len, p2 - p1, n + 1 - p2))
  else
    ret = mma(ret, mmi(len, p1 - p2, n + 1 - p1))
  end
  for j = i + 1, n - 1 do
    p2 = sufa[j + 1]
    len = mmi(len, lcpa[j])
    if p1 < p2 then
      ret = mma(ret, mmi(len, p2 - p1, n + 1 - p2))
    else
      ret = mma(ret, mmi(len, p1 - p2, n + 1 - p1))
    end
  end
end
print(ret)


Measured memory_bytes: 2176