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: Time Limit Exceeded)

using System;
using System.Text;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using static System.Console;
using static System.Math;

namespace AtCorder
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program().Solve(new ConsoleInput(Console.In, ' '));
        }


        public void Solve(ConsoleInput cin)
        {
            var N = cin.ReadInt;
            var S = cin.Read;
            var ans = 0;
            for(int i = 0; i < N; i++)
            {
                var sub = S.Substring(i);
                var tmp = z_algorithm(sub, N - i);
                if (ans < tmp) ans = tmp;
            }
            WriteLine(ans);
        }

        public int z_algorithm(string S, int N)
        {
            var prefix = new int[N];
            var max = 0;
            for (int i = 1, j = 0; i < N; 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 < N && S.Substring(k, 1) == S.Substring(i + k, 1)) k++;
                    prefix[i] = k;
                    j = i;
                }
                if (prefix[i] >= i + 1) prefix[i] = i;
                if (max < prefix[i]) max = prefix[i];
            }
            return max;
        }

        //public class PriorityQueue<T> where T : IComparable
        //{
        //    private IComparer<T> _comparer = null;
        //    private int _type = 0;

        //    private T[] _heap;
        //    private int _sz = 0;

        //    private int _count = 0;

        //    /// <summary>
        //    /// Priority Queue with custom comparer
        //    /// </summary>
        //    public PriorityQueue(int maxSize, IComparer<T> comparer)
        //    {
        //        _heap = new T[maxSize];
        //        _comparer = comparer;
        //    }

        //    /// <summary>
        //    /// Priority queue
        //    /// </summary>
        //    /// <param name="maxSize">max size</param>
        //    /// <param name="type">0: asc, 1:desc</param>
        //    public PriorityQueue(int maxSize, int type = 0)
        //    {
        //        _heap = new T[maxSize];
        //        _type = type;
        //    }

        //    private int Compare(T x, T y)
        //    {
        //        if (_comparer != null) return _comparer.Compare(x, y);
        //        return _type == 0 ? x.CompareTo(y) : y.CompareTo(x);
        //    }

        //    public void Push(T x)
        //    {
        //        _count++;

        //        //node number
        //        var i = _sz++;

        //        while (i > 0)
        //        {
        //            //parent node number
        //            var p = (i - 1) / 2;

        //            if (Compare(_heap[p], x) <= 0) break;

        //            _heap[i] = _heap[p];
        //            i = p;
        //        }

        //        _heap[i] = x;
        //    }

        //    public T Pop()
        //    {
        //        _count--;

        //        T ret = _heap[0];
        //        T x = _heap[--_sz];

        //        int i = 0;
        //        while (i * 2 + 1 < _sz)
        //        {
        //            //children
        //            int a = i * 2 + 1;
        //            int b = i * 2 + 2;

        //            if (b < _sz && Compare(_heap[b], _heap[a]) < 0) a = b;

        //            if (Compare(_heap[a], x) >= 0) break;

        //            _heap[i] = _heap[a];
        //            i = a;
        //        }

        //        _heap[i] = x;

        //        return ret;
        //    }

        //    public int Count()
        //    {
        //        return _count;
        //    }

        //    public T Peek()
        //    {
        //        return _heap[0];
        //    }

        //    public bool Contains(T x)
        //    {
        //        for (int i = 0; i < _sz; i++) if (x.Equals(_heap[i])) return true;
        //        return false;
        //    }

        //    public void Clear()
        //    {
        //        while (this.Count() > 0) this.Pop();
        //    }

        //    public IEnumerator<T> GetEnumerator()
        //    {
        //        var ret = new List<T>();

        //        while (this.Count() > 0)
        //        {
        //            ret.Add(this.Pop());
        //        }

        //        foreach (var r in ret)
        //        {
        //            this.Push(r);
        //            yield return r;
        //        }
        //    }

        //    public T[] ToArray()
        //    {
        //        T[] array = new T[_sz];
        //        int i = 0;

        //        foreach (var r in this)
        //        {
        //            array[i++] = r;
        //        }

        //        return array;
        //    }
        //}

        /*public long C(long n, long r)
        {
            if (r == 0 || r == n) return 1L;
            if (n < r) return 0L;
            long[,] Pascal = new long[n + 1, n + 1];
            Pascal[0, 0] = 1;
            for (long i = 1; i <= n; i++)
            {
                Pascal[i, 0] = 1;
                Pascal[i, i] = 1;
                for (long j = 1; j < n; j++)
                {
                    Pascal[i, j] = Pascal[i - 1, j] + Pascal[i - 1, j - 1];
                }
            }
            return Pascal[n, r];
        }*/

        public class ConsoleInput
        {
            private readonly System.IO.TextReader _stream;
            private char _separator = ' ';
            private Queue<string> inputStream;
            public ConsoleInput(System.IO.TextReader stream, char separator = ' ')
            {
                this._separator = separator;
                this._stream = stream;
                inputStream = new Queue<string>();
            }
            public string Read
            {
                get
                {
                    if (inputStream.Count != 0) return inputStream.Dequeue();
                    string[] tmp = _stream.ReadLine().Split(_separator);
                    for (int i = 0; i < tmp.Length; ++i)
                        inputStream.Enqueue(tmp[i]);
                    return inputStream.Dequeue();
                }
            }
            public string ReadLine { get { return _stream.ReadLine(); } }
            public int ReadInt { get { return int.Parse(Read); } }
            public long ReadLong { get { return long.Parse(Read); } }
            public double ReadDouble { get { return double.Parse(Read); } }
            public string[] ReadStrArray(long N) { var ret = new string[N]; for (long i = 0; i < N; ++i) ret[i] = Read; return ret; }
            public int[] ReadIntArray(long N) { var ret = new int[N]; for (long i = 0; i < N; ++i) ret[i] = ReadInt; return ret; }
            public long[] ReadLongArray(long N) { var ret = new long[N]; for (long i = 0; i < N; ++i) ret[i] = ReadLong; return ret; }
        }
    }
}