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 C# (Mono 4.6.2.0) (judge verdict: Wrong Answer)

using System;
using System.Linq;
using System.Collections.Generic;
using static System.Console;
using System.Text;
using System.IO;
using static System.Math;
namespace AtCoder
{
    class Program
    {
        static public long[] Sarray() { return ReadLine().Split().Select(long.Parse).ToArray(); }
        static public List<long> Slist() { return ReadLine().Split().Select(long.Parse).ToList(); }
        static long Mod = (long)998244353;
        static void Main(string[] args)
        {
            //SetOut(new StreamWriter(OpenStandardOutput()) { AutoFlush = false });
            Solve();
            //Out.Flush();
        }
        static void Solve()
        {
            var N = Sarray()[0];
            var S = ReadLine();
            var sa = new SuffixArray(S);
            var res = 0L;
            for (var i = 0; i < N; ++i)
            {
                for (var j = i + 1; j < N; ++j)
                {
                    var lcp = sa.getLCP(i, j);
                    lcp = Min(lcp, j - i);
                    res = Max(res, lcp);
                }
            }
            WriteLine(res);
        }
        class SuffixArray : IComparer<int>
        {
            int _n, _k;
            int[] _rank;
            int[] _sa;
            int[] _lcp;
            string _S;
            public SuffixArray(string s)
            {
                _n = s.Length;
                _sa = new int[_n + 1];
                _rank = new int[_n + 1];
                _lcp = new int[_n + 1];
                _S = s;
                construct_sa();
                construct_lcp();
            }
            public int Compare(int i, int j)
            {
                if (_rank[i] != _rank[j]) return _rank[i] < _rank[j] ? -1 : 1;
                var ri = (i + _k <= _n) ? _rank[i + _k] : -1;
                var rj = (j + _k <= _n) ? _rank[j + _k] : -1;
                return ri < rj ? -1 : 1;
            }

            void construct_sa()
            {
                for (var i = 0; i <= _n; ++i)
                {
                    _sa[i] = i;
                    _rank[i] = (i < _n) ? _S[i] : -1;
                }
                var tmp = new int[_n + 1];
                for (_k = 1; _k <= _n; _k *= 2)
                {
                    Array.Sort(_sa, Compare);
                    tmp[_sa[0]] = 0;
                    for (var i = 1; i <= _n; ++i)
                        tmp[_sa[i]] = tmp[_sa[i - 1]] + ((Compare(_sa[i - 1], _sa[i]) < 0) ? 1 : 0);
                    for (var i = 0; i <= _n; ++i)
                        _rank[i] = tmp[i];
                }
            }
            int[][] _sparse;
            int[] _height;
            void construct_lcp()
            {
                for (var i = 0; i <= _n; ++i) _rank[_sa[i]] = i;
                var cur = 0;
                _lcp[0] = 0;
                for (var i = 0; i < _n; ++i)
                {
                    var j = _sa[_rank[i] - 1];
                    if (0 < cur) --cur;
                    for (; j + cur < _n && i + cur < _n; ++cur)
                        if (_S[j + cur] != _S[i + cur]) break;
                    _lcp[_rank[i] - 1] = cur;
                }

                var n = _lcp.Length;
                var h = 0;
                while ((1 << h) < n) ++h;
                _sparse = Enumerable.Range(0, h).Select(_ => Enumerable.Range(-1, 1 << h).ToArray()).ToArray();
                _height = new int[n + 1];
                for (var i = 2; i <= n; ++i) _height[i] = _height[i >> 1] + 1;
                for (var i = 0; i < n; ++i) _sparse[0][i] = _lcp[i];
                for (var i = 1; i < h; ++i)
                    for (var j = 0; j < n; ++j)
                        _sparse[i][j] = Min(_sparse[i - 1][j], _sparse[i - 1][Min(j + (1 << (i - 1)), n - 1)]);
            }
            public bool contain(string T)
            {
                var a = 0;
                var b = _n;
                while (b - a > 1)
                {
                    var c = (a + b) / 2;
                    if (_S.Substring(_sa[c]).CompareTo(T) < 0) a = c;
                    else b = c;
                }
                return _S.Substring(_sa[b]).CompareTo(T) == 0;
            }
            public int getLCP(int a, int b) => get(Min(_rank[a], _rank[b]), Max(_rank[a], _rank[b]));
            int get(int a, int b) => Min(_sparse[_height[b - a]][a], _sparse[_height[b - a]][b - (1 << _height[b - a])]);
        }
    }
}