Buggy python submission (verdict: Time Limit Exceeded):
N = int(input())
S = input()


class RMQ:
    inf = 10 ** 9 + 7

    def __init__(self, x):
        # 1-index
        self.n = 2 ** len(x).bit_length()
        self.dat = [self.inf] * (2 * self.n - 1)
        
        for k, v in enumerate(x):
            self.update(k, v)
        
    def update(self, k, v):
        k += self.n - 1
        self.dat[k] = v
        while k > 0:
            k = (k - 1) // 2
            self.dat[k] = min(self.dat[k * 2 + 1], self.dat[k * 2 + 2])
            
    def query(self, a, b, k=0, l=0, r=None):
        # Retrun min [a, b)
        if r is None:
            r = self.n
        
        if b <= l or r <= a:
            return self.inf
        
        if a <= l and r <= b:
            return self.dat[k]
        
        vl = self.query(a, b, k * 2 + 1, l, (l + r) // 2)
        vr = self.query(a, b, k * 2 + 2, (l + r) // 2, r)
        
        return min(vl, vr)
            
    def __repr__(self):
        return ', '.join([str(v) for v in self.dat])


class SuffixArray:
    """Manber & Myers `(O(n log^2 n))`."""
    def __init__(self, s):
        if not isinstance(s, str):
            raise ValueError("Input must be string")

        # Input array
        self.array = s
        self.len = len(s) + 1

        # Suffix array
        self.sa = list(range(self.len))
        self.rank = list(map(ord, self.array)) + [-1]

        # Longest Common Prefix
        self.lcp = [-1] * self.len

        # RMQ
        self.rmq = None

        # Initialize
        self._construct()
        self._calc_lcp()
        self._init_rmq()

    def __getitem__(self, index):
        return self.array[self.sa[index]:]

    def __len__(self):
        return self.len

    def _construct(self):
        """Sorts 2k chars."""
        n = self.len
        k = 1
        while k <= n:
            self.sa.sort(key=lambda x: self._key(x, k))

            tmp = [0] * n
            for i in range(1, n):
                tmp[self.sa[i]] = (tmp[self.sa[i - 1]]
                                   + int(self.compare(i - 1, i, k)))

            self.rank = tmp[:]
            k *= 2

    def _key(self, i, k):
        """Returns key for sort."""
        v1 = self.rank[i]
        v2 = self.rank[i + k] if i + k < self.len else -1
        return (v1, v2)

    def compare(self, i, j, k):
        key1 = self._key(self.sa[i], k)
        key2 = self._key(self.sa[j], k)

        # Compare rank[i] and rank[j]
        if key1[0] != key2[0]:
            return key1[0] < key2[0]

        # Compare rank[i + k] and rank[j + k]
        return key1[1] < key2[1]

    def _calc_lcp(self):
        n = self.len
        for i in range(n):
            self.rank[self.sa[i]] = i

        self.lcp[0] = 0
        h = 0
        for i in range(n - 1):
            j = self.sa[self.rank[i] - 1]

            if h > 0:
                h -= 1
            while j + h < n - 1 and i + h < n - 1:
                if self.array[j + h] != self.array[i + h]:
                    break
                h += 1

            self.lcp[self.rank[i] - 1] = h
            
    def _init_rmq(self):
        lcp = [self.lcp[self.rank[i]] for i in range(self.len)]
        self.rmq = RMQ(lcp)

    def get_lcp(self, i, j):
        if self.rank[i] >= self.rank[j]:
            i, j = j, i

        return self.rmq.query(self.rank[i], self.rank[j])


sa = SuffixArray(S)
res = 0
for i in range(1, len(sa)):
    for j in range(i + 1, len(sa)):
        lcp = sa.get_lcp(i, j)
        res = max(res, lcp)

print(res)


Fixed submission:
N = int(input())
S = input()

def z_algorithm(s):
    n = len(s)
    res = [0] * n
    i = 1
    j = 0
    while i < n:
        while i + j < n and s[j] == s[i + j]:
            j += 1
            
        res[i] = j
        if j == 0:
            i += 1
            continue

        
        k = 1
        while i + k < n and k + res[k] < j:
            res[i + k] = res[k]
            k += 1
        
        i += k
        j -= k
        
    return res


res = 0
for i in range(N):
    lcp = z_algorithm(S[i:])
    for j in range(N - i):
        res = max(res, min(lcp[j], j))
        
print(res)

Buggy python submission (verdict: Time Limit Exceeded):
from collections import defaultdict

N = int(input())
S = input()

B = 10 ** 9 + 7
ans = 0
for l in range(1, N + 1):
    t = B ** l
    
    # First hash
    ah = 0
    bh = 0
    for i in range(l):
        ah = ah * B + ord(S[i])
        bh = bh * B + ord(S[i])
        
    # Roll
    ctr = defaultdict(list)
    for i in range(N - l + 1):
        if not ctr[ah] or ctr[ah][-1] + l <= i:
            ctr[ah].append(i)
        
        if i < N - l:
            ah = ah * B + ord(S[i + l]) - ord(S[i]) * t

    # Check appearance number
    if max(map(len, ctr.values())) >= 2:
        ans = l

print(ans)


Fixed submission:
N = int(input())
S = input()

def z_algorithm(s):
    n = len(s)
    res = [0] * n
    i = 1
    j = 0
    while i < n:
        while i + j < n and s[j] == s[i + j]:
            j += 1
            
        res[i] = j
        if j == 0:
            i += 1
            continue

        
        k = 1
        while i + k < n and k + res[k] < j:
            res[i + k] = res[k]
            k += 1
        
        i += k
        j -= k
        
    return res


res = 0
for i in range(N):
    lcp = z_algorithm(S[i:])
    for j in range(N - i):
        res = max(res, min(lcp[j], j))
        
print(res)

Buggy python submission (verdict: Time Limit Exceeded):
from collections import defaultdict

N = int(input())
S = input()

B = 10 ** 9 + 7
ans = 0
for l in range(1, N + 1):
    t = B ** l
    
    # First hash
    ah = 0
    bh = 0
    for i in range(l):
        ah = ah * B + ord(S[i])
        bh = bh * B + ord(S[i])
        
    # Roll
    ctr = defaultdict(list)
    for i in range(N - l + 1):
        if not ctr[ah] or ctr[ah][-1] + l <= i:
            ctr[ah].append(i)
        
        if i < N - l:
            ah = ah * B + ord(S[i + l]) - ord(S[i]) * t

    # Check appearance number
    if max(map(len, ctr.values())) >= 2:
        ans = l

print(ans)


Fixed submission:
N = int(input())
S = input()

def z_algorithm(s):
    n = len(s)
    res = [0] * n
    i = 1
    j = 0
    while i < n:
        while i + j < n and s[j] == s[i + j]:
            j += 1
            
        res[i] = j
        if j == 0:
            i += 1
            continue

        
        k = 1
        while i + k < n and k + res[k] < j:
            res[i + k] = res[k]
            k += 1
        
        i += k
        j -= k
        
    return res


res = 0
for i in range(N):
    lcp = z_algorithm(S[i:])
    for j in range(N - i):
        res = max(res, min(lcp[j], j))
        
print(res)