Problem:
Score : 500 points

Problem Statement

Given is a string S of length N.

Find the maximum length of a non-empty string that occurs twice or more in S as contiguous substrings without overlapping.

More formally, find the maximum positive integer len such that there exist integers l_1 and l_2 ( 1 \leq l_1, l_2 \leq N - len + 1 ) that satisfy the following:

l_1 + len \leq l_2

S[l_1+i] = S[l_2+i] (i = 0, 1, ..., len - 1)

If there is no such integer len, print 0.

Constraints

2 \leq N \leq 5 \times 10^3

|S| = N

S consists of lowercase English letters.

Input

Input is given from Standard Input in the following format:

N
S

Output

Print the maximum length of a non-empty string that occurs twice or more in S as contiguous substrings without overlapping. If there is no such non-empty string, print 0 instead.

Sample Input 1

5
ababa

Sample Output 1

2

The strings satisfying the conditions are: a, b, ab, and ba. The maximum length among them is 2, which is the answer.
Note that aba occurs twice in S as contiguous substrings, but there is no pair of integers l_1 and l_2 mentioned in the statement such that l_1 + len \leq l_2.

Sample Input 2

2
xy

Sample Output 2

0

No non-empty string satisfies the conditions.

Sample Input 3

13
strangeorange

Sample Output 3

5

Solution in LuaJIT (2.0.4) (judge verdict: Accepted)

local mmi, mma = math.min, math.max
local TranSufA = {}
TranSufA.makeSufA = function(self)
  self.sufa = {}
  local n = #self.str
  local idx, tbl1, tbl2 = self.sufa, {}, {}
  for i = 1, n do
    idx[i] = i
    tbl1[i] = self.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
    -- print("A", table.concat(idx, " "))
    -- print("V", table.concat(v, " "))
    -- print("VD", table.concat(vd, " "))
    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 minormap = {}
    local cur = 0
    for i = 1, n do
      local minor = v[stepdst[idx[i]]]
      if minormap[minor] then minormap[minor] = minormap[minor] + 1
      else minormap[minor] = 1
      end
      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 minortbl = {}
          for k, _u in pairs(minormap) do table.insert(minortbl, k) end
          table.sort(minortbl)
          local offset = 0
          for i = 1, #minortbl do
            local tmp = minormap[minortbl[i]]
            minormap[minortbl[i]] = i
            minortbl[i] = offset -- reuse
            offset = offset + tmp
          end
          local t = {}
          for j = left, right do
            t[j - left + 1] = 0
          end
          for j = left, right do
            local idxj = idx[j]
            local minor_idx = minormap[v[stepdst[idxj]]]
            local ofst = minortbl[minor_idx]
            ofst = ofst + 1
            minortbl[minor_idx] = ofst
            t[ofst] = idxj
            vd[idxj] = cur + minor_idx
          end
          cur = cur + #minortbl
          for j = left, right do
            idx[j] = t[j - left + 1]
          end
          -- local t = {}
          -- for j = left, right do
          --   t[j - left + 1] = idx[j]
          -- end
          -- print("Sort", #t)
          -- 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
          minormap = {}
        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()
  self:makeLCPA()
end

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

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)