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: Accepted)

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using static System.Math;
using MethodImplOptions = System.Runtime.CompilerServices.MethodImplOptions;
using MethodImplAttribute = System.Runtime.CompilerServices.MethodImplAttribute;

public static class P
{
    public static void Main()
    {
        int n = int.Parse(Console.ReadLine());
        var s = Console.ReadLine();
        int curMaxLength = 0;
        var hash = new RollingHash(s);
        for (int i = 0; i < s.Length; i++)
            for (int j = i; j < s.Length; j++)
                while (curMaxLength < j - i && j + curMaxLength < n && hash.Slice(i, curMaxLength + 1) == hash.Slice(j, curMaxLength + 1)) curMaxLength++;
        Console.WriteLine(curMaxLength);
    }
}

class RollingHash
{
    const ulong MOD1 = 998244353;
    const ulong MOD2 = 1000000007;
    const ulong POSITIVIZER1 = MOD1 << 31;
    const ulong POSITIVIZER2 = MOD2 << 31;
    static uint Base1;
    static uint Base2;
    static ulong[] powMemo1 = new ulong[500000 + 1];
    static ulong[] powMemo2 = new ulong[500000 + 1];

    static RollingHash()
    {
        Base1 = (uint)new Random().Next(129, int.MaxValue);
        powMemo1[0] = 1;
        for (int i = 1; i < powMemo1.Length; i++)
            powMemo1[i] = (powMemo1[i - 1] * Base1) % MOD1;
        Base2 = (uint)new Random().Next(129, int.MaxValue);
        powMemo2[0] = 1;
        for (int i = 1; i < powMemo2.Length; i++)
            powMemo2[i] = (powMemo2[i - 1] * Base2) % MOD2;
    }

    ulong[] hash1;
    ulong[] hash2;

    public RollingHash(string s)
    {
        hash1 = new ulong[s.Length + 1];
        for (int i = 0; i < s.Length; i++)
            hash1[i + 1] = (hash1[i] * Base1 + s[i]) % MOD1;
        hash2 = new ulong[s.Length + 1];
        for (int i = 0; i < s.Length; i++)
            hash2[i + 1] = (hash2[i] * Base2 + s[i]) % MOD2;
    }

    public ulong Slice(int begin, int length)
    {
        return 
            ((hash1[begin + length] + POSITIVIZER1 - hash1[begin] * powMemo1[length]) % MOD1) << 32 |
            ((hash2[begin + length] + POSITIVIZER2 - hash2[begin] * powMemo2[length]) % MOD2);
    }
}