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


class RollingHash(object):
    def __init__(self, S, base, mod):
        self.mod = mod
        l = len(S)

        self.hash_lst = [0] * (l + 1)
        self.pow_lst = [1] * (l + 1)

        for i in range(l):
            self.hash_lst[i + 1] = (self.hash_lst[i] * base + ord(S[i])) % mod
            self.pow_lst[i + 1] = (self.pow_lst[i] * base) % mod

    def get_hash(self, left, right):
        return (self.hash_lst[right] - self.hash_lst[left] * self.pow_lst[right - left] % self.mod) % self.mod

base = 1007
mod = pow(10, 9) + 7
rh = RollingHash(S, base, mod)

l = len(S)

res = 0
for i in range(N):
    for j in range(i + 1, N):
        min_len = min(N - j, j - i)

        left = i
        right = i + min_len

        if S[i] == S[j]:
            while left < right:
                mid = (left + right) // 2
                h1 = rh.get_hash(i, mid)
                h2 = rh.get_hash(j, j + (mid - i))

                if h1 == h2:
                    left = mid + 1
                else:
                    right = mid - 1

            res = max(res, left - i)

print(res)


Fixed submission:
class RollingHash(object):
    def __init__(self, S, base, mod):
        self.mod = mod
        l = len(S)

        self.hash_lst = [0] * (l + 1)
        self.pow_lst = [1] * (l + 1)

        for i in range(l):
            self.hash_lst[i + 1] = (self.hash_lst[i] * base + ord(S[i])) % mod
            self.pow_lst[i + 1] = (self.pow_lst[i] * base) % mod

    def get_hash(self, left, right):
        return (self.hash_lst[right] - self.hash_lst[left] * self.pow_lst[right - left] % self.mod) % self.mod


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

base = 1007
mod = pow(10, 9) + 7
rh = RollingHash(S, base, mod)

left = 0
right = N // 2

while left <= right:
    mid = (left + right) // 2

    found_hash = {}
    found = False
    for i in range(N - mid + 1):
        h = rh.get_hash(i, i + mid)

        if h not in found_hash:
            found_hash[h] = i
        else:
            if found_hash[h] <= i - mid:
                found = True
                break

    if found:
        left = mid + 1
    else:
        right = mid - 1


print(right)

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


def suffix_array(S):
    suffix = []
    for i in range(len(S)):
        suffix.append((S[i:], i))

    return sorted(suffix)


def lcp_array(S, SA):
    SA_idx = [i for _, i in SA]
    l = len(S)

    rank = [0] * l
    for i in range(l):
        rank[SA_idx[i]] = i

    h = 0
    LCP = [0] * l
    for i in range(l):
        j = SA_idx[rank[i] - 1]

        if h > 0:
            h -= 1

        while j + h < l and i + h < l:
            if S[j + h] != S[i + h]:
                break
            h += 1

        LCP[rank[i] - 1] = h

    return LCP


SA = suffix_array(S)
LCP = lcp_array(S, SA)

idx2SAidx = [0] * N

for i, (_, j) in enumerate(SA):
    idx2SAidx[j] = i

res = 0
for i in range(N):
    for j in range(i + 1, N):
        k1 = idx2SAidx[i]
        k2 = idx2SAidx[j]

        if k2 < k1:
            k1, k2 = k2, k1

        min_ = N
        for k in range(k1, k2):
            min_ = min(min_, LCP[k])

        if min_ > (j - i):
            min_ = j - i

        res = max(res, min_)


print(res)



Fixed submission:
class RollingHash(object):
    def __init__(self, S, base, mod):
        self.mod = mod
        l = len(S)

        self.hash_lst = [0] * (l + 1)
        self.pow_lst = [1] * (l + 1)

        for i in range(l):
            self.hash_lst[i + 1] = (self.hash_lst[i] * base + ord(S[i])) % mod
            self.pow_lst[i + 1] = (self.pow_lst[i] * base) % mod

    def get_hash(self, left, right):
        return (self.hash_lst[right] - self.hash_lst[left] * self.pow_lst[right - left] % self.mod) % self.mod


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

base = 1007
mod = pow(10, 9) + 7
rh = RollingHash(S, base, mod)

left = 0
right = N // 2

while left <= right:
    mid = (left + right) // 2

    found_hash = {}
    found = False
    for i in range(N - mid + 1):
        h = rh.get_hash(i, i + mid)

        if h not in found_hash:
            found_hash[h] = i
        else:
            if found_hash[h] <= i - mid:
                found = True
                break

    if found:
        left = mid + 1
    else:
        right = mid - 1


print(right)

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

# 文字とindexのマップ
dic = {}
for i, n in enumerate(S):
    if n not in dic:
        dic[n] = [i]
    else:
        dic[n].append(i)


ret_max = 0
for i in range(N):
    index = []
    temp_max = 0
    for j in range(N):
        # target = S[i:j + 1]
        target = S[i]

        if target in dic:
            idx_lst = dic[target].copy()
            # print(idx_lst, i, target)

            idx_lst.remove(i)

            if idx_lst:
                temp_max = 1
            else:
                temp_max = 0
        else:
            continue

        cnt = 1
        if idx_lst:
            next_idx = idx_lst[0]
        else:
            next_idx = 0
        while idx_lst:
            for idx in idx_lst:
                if j + 1 + cnt <= next_idx and S[idx:idx + 1 + cnt] == S[i:j + 1 + cnt] and idx + 1 + cnt <= N:
                    # print(idx, S[idx:idx + 1 + cnt], S[i:j + 1 + cnt])
                    temp_max = 1 + cnt
                else:
                    idx_lst.remove(idx)

            cnt += 1

        ret_max = max(ret_max, temp_max)


print(ret_max)

Fixed submission:
class RollingHash(object):
    def __init__(self, S, base, mod):
        self.mod = mod
        l = len(S)

        self.hash_lst = [0] * (l + 1)
        self.pow_lst = [1] * (l + 1)

        for i in range(l):
            self.hash_lst[i + 1] = (self.hash_lst[i] * base + ord(S[i])) % mod
            self.pow_lst[i + 1] = (self.pow_lst[i] * base) % mod

    def get_hash(self, left, right):
        return (self.hash_lst[right] - self.hash_lst[left] * self.pow_lst[right - left] % self.mod) % self.mod


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

base = 1007
mod = pow(10, 9) + 7
rh = RollingHash(S, base, mod)

left = 0
right = N // 2

while left <= right:
    mid = (left + right) // 2

    found_hash = {}
    found = False
    for i in range(N - mid + 1):
        h = rh.get_hash(i, i + mid)

        if h not in found_hash:
            found_hash[h] = i
        else:
            if found_hash[h] <= i - mid:
                found = True
                break

    if found:
        left = mid + 1
    else:
        right = mid - 1


print(right)