Buggy python submission (verdict: Time Limit Exceeded):
"""
連続する部分文字列の種類は多くてN**2個ある。
N<=5000という制約から考えて、これらの連続する部分文字列に対して全探索ができる。
問題は、各部分文字列が条件を満たしているかどうかをどうやって調べるかだ。
これは、各部分文字列種ごとにどの区間で出現するかをメモしておけば良い。
dic["hogehoge"] => "hogehoge"という連続部分文字列が出現する区間をメモした配列
dic["hogehoge"] = [(0,7),(2,9),(6,13),(10,17)]　といった具合だ。
dic[hogehoge][x][1]とdic[hogehoge][x+1][1]が重複しているかを調べることで条件を満たすかどうかを判定できる。
区間(i,j)のjで昇順並びになっている必要があるが、実直にi,jでループして連続部分文字列を探索すれば、自然と昇順でメモされる。
計算量はO(N**2)で間に合う。
"""
N = int(input())
S = tuple(input())

dic = {}
#ここがTLEの原因になっている。
for i in range(N):
    for j in range(N):
        if S[i:j+1] not in dic:
            dic[S[i:j+1]] = []
        dic[S[i:j+1]].append((i,j))

ans = 0
for k,v in dic.items():
    if len(v) > 1:
        for i in range(1,len(v)):
            if v[i][0]<=v[i-1][1]:
                break
        else:
            ans = max(ans,len(k))
print(ans)

Fixed submission:
"""
X文字が条件にあてはまるとき、X-1文字も条件に当てはまる。したがって、二分探索を使うのがよい。
一回の探索に、O(N**2)かかる。
探索はO(logN)回行うため、全体でO(N**2logN)で、ギリ間に合う。
"""
N = int(input())
S = input()

def is_ok(X):
    dic = {}
    for i in range(N+1-X):
        sub = S[i:i+X]
        if sub not in dic:
            dic[sub] = (i,i+X-1)
        else:
            if dic[sub][1] < i:
                return True
    return False

def bisearch(high,low):
    while high - low > 1:
        mid = (high+low)//2
        if is_ok(mid):
            low = mid
        else:
            high = mid
    return low
print(bisearch(N,0))

Buggy python submission (verdict: Time Limit Exceeded):
"""
連続する部分文字列の種類は多くてN**2個ある。
N<=5000という制約から考えて、これらの連続する部分文字列に対して全探索ができる。
問題は、各部分文字列が条件を満たしているかどうかをどうやって調べるかだ。
これは、各部分文字列種ごとにどの区間で出現するかをメモしておけば良い。
dic["hogehoge"] => "hogehoge"という連続部分文字列が出現する区間をメモした配列
dic["hogehoge"] = [(0,7),(2,9),(6,13),(10,17)]　といった具合だ。
dic[hogehoge][x][1]とdic[hogehoge][x+1][1]が重複しているかを調べることで条件を満たすかどうかを判定できる。
区間(i,j)のjで昇順並びになっている必要があるが、実直にi,jでループして連続部分文字列を探索すれば、自然と昇順でメモされる。
計算量はO(N**2)で間に合う。
"""
N = int(input())
S = input()

dic = {}
for i in range(N):
    for j in range(i,N):
        if S[i:j+1] not in dic:
            dic[S[i:j+1]] = []
        dic[S[i:j+1]].append((i,j))

ans = 0
for k,v in dic.items():
    if len(v) > 1:
        for i in range(1,len(v)):
            if v[i][0]<=v[i-1][1]:
                break
        else:
            ans = max(ans,len(k))
print(ans)




Fixed submission:
"""
X文字が条件にあてはまるとき、X-1文字も条件に当てはまる。したがって、二分探索を使うのがよい。
一回の探索に、O(N**2)かかる。
探索はO(logN)回行うため、全体でO(N**2logN)で、ギリ間に合う。
"""
N = int(input())
S = input()

def is_ok(X):
    dic = {}
    for i in range(N+1-X):
        sub = S[i:i+X]
        if sub not in dic:
            dic[sub] = (i,i+X-1)
        else:
            if dic[sub][1] < i:
                return True
    return False

def bisearch(high,low):
    while high - low > 1:
        mid = (high+low)//2
        if is_ok(mid):
            low = mid
        else:
            high = mid
    return low
print(bisearch(N,0))

Buggy python submission (verdict: Time Limit Exceeded):
"""
連続する部分文字列の種類は多くてN**2個ある。
N<=5000という制約から考えて、これらの連続する部分文字列に対して全探索ができる。
問題は、各部分文字列が条件を満たしているかどうかをどうやって調べるかだ。
これは、各部分文字列種ごとにどの区間で出現するかをメモしておけば良い。
dic["hogehoge"] => "hogehoge"という連続部分文字列が出現する区間をメモした配列
dic["hogehoge"] = [(0,7),(2,9),(6,13),(10,17)]　といった具合だ。
dic[hogehoge][x][1]とdic[hogehoge][x+1][1]が重複しているかを調べることで条件を満たすかどうかを判定できる。
区間(i,j)のjで昇順並びになっている必要があるが、実直にi,jでループして連続部分文字列を探索すれば、自然と昇順でメモされる。
計算量はO(N**2)で間に合う。
"""
N = int(input())
S = input()

dic = {}
for i in range(N):
    for j in range(N):
        if S[i:j+1] not in dic:
            dic[S[i:j+1]] = []
        dic[S[i:j+1]].append((i,j))

ans = 0
for k,v in dic.items():
    if len(v) > 1:
        for i in range(1,len(v)):
            if v[i][0]<=v[i-1][1]:
                break
        else:
            ans = max(ans,len(k))
print(ans)




Fixed submission:
"""
X文字が条件にあてはまるとき、X-1文字も条件に当てはまる。したがって、二分探索を使うのがよい。
一回の探索に、O(N**2)かかる。
探索はO(logN)回行うため、全体でO(N**2logN)で、ギリ間に合う。
"""
N = int(input())
S = input()

def is_ok(X):
    dic = {}
    for i in range(N+1-X):
        sub = S[i:i+X]
        if sub not in dic:
            dic[sub] = (i,i+X-1)
        else:
            if dic[sub][1] < i:
                return True
    return False

def bisearch(high,low):
    while high - low > 1:
        mid = (high+low)//2
        if is_ok(mid):
            low = mid
        else:
            high = mid
    return low
print(bisearch(N,0))