Buggy python submission (verdict: Time Limit Exceeded):
from collections import deque
N = int(input())
S = str(input())
T = deque(list(S))
#print(T)

#Z = [0 for _ in range(N)]
#Z[0] = N

def momokuroz(s): #z-algorithm
  Max = 0
  n = len(s)
  z = [0 for _ in range(n)]
  z[0] = n
  i = 1; j = 0
  while i < n: #iは基準の位置。
    while i+j < n and s[i+j] == s[j]:
      j += 1 #何文字一致してるかなー？
    z[i] = min(i,j) #jが一致している文字数。
    Max = max(Max,z[i])
    if j == 0:
      i += 1
      continue
    
    k = 1 #途中でjを超えていたらその先は別途見る必要がある。
    #kが一致してる文字数ぴったしだとその先は不明。k<j
    #j文字一致しているところを参照したいが飛び出していたらそこは不明。k+Z[k]<j
    while k<j and k+z[k] <j:
      z[i+k] = z[k]
      k += 1
    
    i += k #基準位置を動かす
    j -= k #このjまでは一致しているのは確定なので上のWhileに戻ってチェック。
  #MAX = max(z[1:])
  #print(Max,s,z)
  return Max

ans = 0    
for i in range(N-1):
  temp = "".join(T)
  a = momokuroz(temp)
  ans = max(ans,a)
  T.popleft()


print(ans)

Fixed submission:
class RollingHash():
    def __init__(self, s, base, mod):
        self.mod = mod
        self.pw = pw = [1]*(len(s)+1)

        l = len(s)
        self.h = h = [0]*(l+1)

        v = 0
        for i in range(l):
            h[i+1] = v = (v * base + ord(s[i])) % mod
        v = 1
        for i in range(l):
            pw[i+1] = v = v * base % mod
    def get(self, l, r): #S[l:r]indexでいうl文字目からr-l文字目。r-l文字。
        return (self.h[r] - self.h[l] * self.pw[r-l]) % self.mod

    def check(self, d):
        ma = {}
        for i in range(N-d+1): #0からN-dまで
            p = self.get(i,i+d)
            if p in ma:
                if i - ma[p] >= d: #d文字以上離れているか
                  return True
            else:
                ma[p] = i
        return False
            
              
N =int(input())
S = str(input())

base1 = 1007
mod1 = 10**9+7

RH = RollingHash(S,base1,mod1)

left = 0
right = N//2+1
while right - left >1:
  mid = (left+right)//2
  if RH.check(mid): #こちらがokの方
    left = mid
  else:
    right = mid

print(left)

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

Z = [0 for _ in range(N)]
Z[0] = N

def momokuroz(s):
  Max = 0
  n = len(s)
  z = [0 for _ in range(n)]
  z[0] = n
  i = 1; j = 0
  while i < n: #iは基準の位置。
    while i+j < n and s[i+j] == s[j]:
      j += 1 #何文字一致してるかなー？
    z[i] = min(i,j) #jが一致している文字数。
    Max = max(Max,z[i])
    if j == 0:
      i += 1
      continue
    
    k = 1 #途中でjを超えていたらその先は別途見る必要がある。
    #kが一致してる文字数ぴったしだとその先は不明。k<j
    #j文字一致しているところを参照したいが飛び出していたらそこは不明。k+Z[k]<j
    while k<j and k+z[k] <j:
      z[i+k] = z[k]
      k += 1
    
    i += k #基準位置を動かす
    j -= k #このjまでは一致しているのは確定なので上のWhileに戻ってチェック。
  #MAX = max(z[1:])
  #print(Max,s,z)
  return Max

ans = 0    
for i in range(N-1):
  temp = S[i:]
  a = momokuroz(temp)
  ans = max(ans,a)


print(ans)

Fixed submission:
class RollingHash():
    def __init__(self, s, base, mod):
        self.mod = mod
        self.pw = pw = [1]*(len(s)+1)

        l = len(s)
        self.h = h = [0]*(l+1)

        v = 0
        for i in range(l):
            h[i+1] = v = (v * base + ord(s[i])) % mod
        v = 1
        for i in range(l):
            pw[i+1] = v = v * base % mod
    def get(self, l, r): #S[l:r]indexでいうl文字目からr-l文字目。r-l文字。
        return (self.h[r] - self.h[l] * self.pw[r-l]) % self.mod

    def check(self, d):
        ma = {}
        for i in range(N-d+1): #0からN-dまで
            p = self.get(i,i+d)
            if p in ma:
                if i - ma[p] >= d: #d文字以上離れているか
                  return True
            else:
                ma[p] = i
        return False
            
              
N =int(input())
S = str(input())

base1 = 1007
mod1 = 10**9+7

RH = RollingHash(S,base1,mod1)

left = 0
right = N//2+1
while right - left >1:
  mid = (left+right)//2
  if RH.check(mid): #こちらがokの方
    left = mid
  else:
    right = mid

print(left)

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

Z = [0 for _ in range(N)]
Z[0] = N

def momokuroz(s):
  n = len(s)
  z = [0 for _ in range(n)]
  z[0] = n
  i = 1; j = 0
  while i < n: #iは基準の位置。
    while i+j < n and s[i+j] == s[j]:
      j += 1 #何文字一致してるかなー？
    z[i] = min(i,j) #jが一致している文字数。
    if j == 0:
      i += 1
      continue
    
    k = 1 #途中でjを超えていたらその先は別途見る必要がある。
    #kが一致してる文字数ぴったしだとその先は不明。k<j
    #j文字一致しているところを参照したいが飛び出していたらそこは不明。k+Z[k]<j
    while k<j and k+z[k] <j:
      z[i+k] = z[k]
      k += 1
    
    i += k #基準位置を動かす
    j -= k #このjまでは一致しているのは確定なので上のWhileに戻ってチェック。
  #MAX = max(z[1:])
  #print(MAX,s)
  return z

ans = 0    
for i in range(N-1):
  temp = S[i:]
  t = momokuroz(temp)
  #print(t,max(t[1:]))
  ans = max(ans,max(t[1:]))    


print(ans)

Fixed submission:
class RollingHash():
    def __init__(self, s, base, mod):
        self.mod = mod
        self.pw = pw = [1]*(len(s)+1)

        l = len(s)
        self.h = h = [0]*(l+1)

        v = 0
        for i in range(l):
            h[i+1] = v = (v * base + ord(s[i])) % mod
        v = 1
        for i in range(l):
            pw[i+1] = v = v * base % mod
    def get(self, l, r): #S[l:r]indexでいうl文字目からr-l文字目。r-l文字。
        return (self.h[r] - self.h[l] * self.pw[r-l]) % self.mod

    def check(self, d):
        ma = {}
        for i in range(N-d+1): #0からN-dまで
            p = self.get(i,i+d)
            if p in ma:
                if i - ma[p] >= d: #d文字以上離れているか
                  return True
            else:
                ma[p] = i
        return False
            
              
N =int(input())
S = str(input())

base1 = 1007
mod1 = 10**9+7

RH = RollingHash(S,base1,mod1)

left = 0
right = N//2+1
while right - left >1:
  mid = (left+right)//2
  if RH.check(mid): #こちらがokの方
    left = mid
  else:
    right = mid

print(left)

Buggy python submission (verdict: Time Limit Exceeded):
class RollingHash():
    def __init__(self, s, base, mod):
        self.mod = mod
        self.pw = pw = [1]*(len(s)+1)

        l = len(s)
        self.h = h = [0]*(l+1)

        v = 0
        for i in range(l):
            h[i+1] = v = (v * base + ord(s[i])) % mod
        v = 1
        for i in range(l):
            pw[i+1] = v = v * base % mod
    def get(self, l, r): #S[l:r]indexでいうl文字目からr-1文字目
        return (self.h[r] - self.h[l] * self.pw[r-l]) % self.mod
      
N =int(input())
S = str(input())

base1 = 1007
mod1 = 10**9+7

RH = RollingHash(S,base1,mod1)

#print(a,b)
ans = 0
for i in range(N-1):
  for j in range(i+1,N):
    low = 0
    high = min(j-i,N-j)+1
    while abs(low-high)>1:
      mid = (low+high)//2
      if RH.get(i,i+mid) != RH.get(j,j+mid):
        high = mid
      else:
        low = mid
    ans = max(ans,low)
    #print(i,j,low)
print(ans)      


Fixed submission:
class RollingHash():
    def __init__(self, s, base, mod):
        self.mod = mod
        self.pw = pw = [1]*(len(s)+1)

        l = len(s)
        self.h = h = [0]*(l+1)

        v = 0
        for i in range(l):
            h[i+1] = v = (v * base + ord(s[i])) % mod
        v = 1
        for i in range(l):
            pw[i+1] = v = v * base % mod
    def get(self, l, r): #S[l:r]indexでいうl文字目からr-l文字目。r-l文字。
        return (self.h[r] - self.h[l] * self.pw[r-l]) % self.mod

    def check(self, d):
        ma = {}
        for i in range(N-d+1): #0からN-dまで
            p = self.get(i,i+d)
            if p in ma:
                if i - ma[p] >= d: #d文字以上離れているか
                  return True
            else:
                ma[p] = i
        return False
            
              
N =int(input())
S = str(input())

base1 = 1007
mod1 = 10**9+7

RH = RollingHash(S,base1,mod1)

left = 0
right = N//2+1
while right - left >1:
  mid = (left+right)//2
  if RH.check(mid): #こちらがokの方
    left = mid
  else:
    right = mid

print(left)