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

sa = [i for i in range(N+1)]
rank = [ord(c) for c in S] + [-1]

k = -1
key = lambda i: (rank[i], rank[i+k] if i+k <= N else -1)

def construct_sa():
    global k,rank
    k = 1
    tmp = [0] * (N+1)
    while k <= N:
        sa.sort(key=key)
        tmp[sa[0]] = 0
        for i in range(1,N+1):
            tmp[sa[i]] = tmp[sa[i-1]] + (key(sa[i-1]) < key(sa[i]))
        rank = [r for r in tmp]
        k *= 2

construct_sa()


def exist_twice(s):
    n = len(s)
    l,r = 0,N
    while r-l > 1:
        m = (l+r)//2
        if S[sa[m]:sa[m]+n] < s:
            l = m
        else:
            r = m
    if S[sa[r]:sa[r]+n] != s:
        return False
    minsa = maxsa = sa[r]
    while r <= N and S[sa[r]:sa[r]+n] == s:
        minsa = min(minsa, sa[r])
        maxsa = max(maxsa, sa[r])
        if maxsa - minsa >= n:
            return True
        r += 1
    return False

def is_ok(l):
    if l<=0: return True
    for i in range(N-l+1):
        if exist_twice(S[i:i+l]):
            return True
    return False

ok = 0
ng = N//2+1
while ng-ok > 1:
    m = (ng+ok)//2
    if is_ok(m):
        ok = m
    else:
        ng = m
print(ok)

Fixed submission:
N = int(input())
S = input()
from collections import deque

def is_ok(l):
    hs = set()
    q = deque()
    for i in range(N-l+1):
        h = hash(S[i:i+l])
        if h in hs:
            return True
        q.append(h)
        if len(q) >= l:
            hs.add(q.popleft())
    return False

ok = 0
ng = N//2+1
while ng-ok > 1:
    m = (ng+ok)//2
    if is_ok(m):
        ok = m
    else:
        ng = m
print(ok)

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

sa = [i for i in range(N+1)]
rank = [ord(c) for c in S] + [-1]

k = -1
key = lambda i: (rank[i], rank[i+k] if i+k <= N else -1)

def construct_sa():
    global k,rank
    k = 1
    tmp = [0] * (N+1)
    while k <= N:
        sa.sort(key=key)
        tmp[sa[0]] = 0
        for i in range(1,N+1):
            tmp[sa[i]] = tmp[sa[i-1]] + (key(sa[i-1]) < key(sa[i]))
        rank = [r for r in tmp]
        k *= 2

construct_sa()


def exist_twice(s):
    n = len(s)
    l,r = 0,N
    while r-l > 1:
        m = (l+r)//2
        if S[sa[m]:sa[m]+n] < s:
            l = m
        else:
            r = m
    if S[sa[r]:sa[r]+n] != s:
        return False
    minsa = maxsa = sa[r]
    while r <= N and S[sa[r]:sa[r]+n] == s:
        minsa = min(minsa, sa[r])
        maxsa = max(maxsa, sa[r])
        if maxsa - minsa >= n:
            return True
        r += 1
    return False

def is_ok(l):
    if l<=0: return True
    for i in range(N-l+1):
        if exist_twice(S[i:i+l]):
            return True
    return False

ok = 0
ng = N//2+1
while ng-ok > 1:
    m = (ng+ok)//2
    if is_ok(m):
        ok = m
    else:
        ng = m
print(ok)

Fixed submission:
N = int(input())
S = input()
from collections import deque

def is_ok(l):
    hs = set()
    q = deque()
    for i in range(N-l+1):
        h = hash(S[i:i+l])
        if h in hs:
            return True
        q.append(h)
        if len(q) >= l:
            hs.add(q.popleft())
    return False

ok = 0
ng = N//2+1
while ng-ok > 1:
    m = (ng+ok)//2
    if is_ok(m):
        ok = m
    else:
        ng = m
print(ok)