Buggy python submission (verdict: Time Limit Exceeded):
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())

n = ni()
s = ns()

dic = {}
strs = set()

for st in range(n):
    stri = s[st]
    if not stri in strs:
        dic[stri] = [st, -1]
        strs.add(stri)
    else:
        dic[stri][1] = st

    for ed in range(st+1, min(st+n//2+2, n)):
        stri += s[ed]
        if not stri in strs:
            dic[stri] = [st, -1]
            strs.add(stri)
        else:
            dic[stri][1] = st

strlist = list(strs)
strlist.sort(key=lambda x: len(x), reverse=True)
ans = 0
for stri in strlist:
    val = dic[stri]
    leng = len(stri)
    if val[1] - val[0] >= len(stri):
        ans = len(stri)
        break

print(ans)

Fixed submission:
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())


class RollingHash(object):
    def __init__(self, source: str, base=10**9+7, mod=(1<<64)-1):
        self.source = source
        self.base = base
        self.mod = mod
        self.hash_from_zero_to = self._get_hash_from_zero_to()
        self.base_pow = self._get_base_pow()

    # 文字列sourceの区間 [0, right) のハッシュ値を前計算する
    def _get_hash_from_zero_to(self):
        hash_from_zero_to = [0]
        for si in self.source:
            hash_from_zero_to.append((hash_from_zero_to[-1] * self.base + ord(si)) % self.mod)

        return hash_from_zero_to

    # base値をi回掛けてmodをとった値を前計算する
    def _get_base_pow(self):
        base_pow = [1]
        for i in range(len(self.source)):
            base_pow.append(base_pow[-1] * self.base % self.mod)

        return base_pow

    # 文字列sourceの区間 [left, right) のハッシュを取得する
    def get_hash(self, left: int, right: int):
        return (self.hash_from_zero_to[right]
                - self.hash_from_zero_to[left] * self.base_pow[right - left]) % self.mod


def binsearch(func, rh: RollingHash, ok: int, ng: int):
    source_length = len(rh.source)
    while ng - ok > 1:
        mid = (ok + ng) // 2
        if func(rh, mid, source_length):
            ok = mid
        else:
            ng = mid

    return ok


# 題意を満たす長さtarget_lengthの文字列があるか判定
def judge(rh: RollingHash, target_length: int, source_length: int):
    memo = {}
    for start_idx in range(source_length - target_length + 1):
        cur_hash = rh.get_hash(start_idx, start_idx + target_length)
        if cur_hash in memo:
            if start_idx - memo[cur_hash] >= target_length:
                return True
        else:
            memo[cur_hash] = start_idx

    return False


n = ni()
s = ns()

rh = RollingHash(s)
print(binsearch(judge, rh, 0, n//2 + 2))

Buggy python submission (verdict: Time Limit Exceeded):
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())

n = ni()
s = ns()

dic = {}
strs = set()

for st in range(n):
    stri = s[st]
    if not stri in strs:
        dic[stri] = [st, -1]
        strs.add(stri)
    else:
        dic[stri][1] = st

    for ed in range(st+1, min(st+n//2+2, n)):
        stri += s[ed]
        if not stri in strs:
            dic[stri] = [st, -1]
            strs.add(stri)
        else:
            dic[stri][1] = st

ans = 0
for key, val in dic.items():
    if val[1] - val[0] >= len(key):
        ans = max(ans, len(key))

print(ans)

Fixed submission:
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())


class RollingHash(object):
    def __init__(self, source: str, base=10**9+7, mod=(1<<64)-1):
        self.source = source
        self.base = base
        self.mod = mod
        self.hash_from_zero_to = self._get_hash_from_zero_to()
        self.base_pow = self._get_base_pow()

    # 文字列sourceの区間 [0, right) のハッシュ値を前計算する
    def _get_hash_from_zero_to(self):
        hash_from_zero_to = [0]
        for si in self.source:
            hash_from_zero_to.append((hash_from_zero_to[-1] * self.base + ord(si)) % self.mod)

        return hash_from_zero_to

    # base値をi回掛けてmodをとった値を前計算する
    def _get_base_pow(self):
        base_pow = [1]
        for i in range(len(self.source)):
            base_pow.append(base_pow[-1] * self.base % self.mod)

        return base_pow

    # 文字列sourceの区間 [left, right) のハッシュを取得する
    def get_hash(self, left: int, right: int):
        return (self.hash_from_zero_to[right]
                - self.hash_from_zero_to[left] * self.base_pow[right - left]) % self.mod


def binsearch(func, rh: RollingHash, ok: int, ng: int):
    source_length = len(rh.source)
    while ng - ok > 1:
        mid = (ok + ng) // 2
        if func(rh, mid, source_length):
            ok = mid
        else:
            ng = mid

    return ok


# 題意を満たす長さtarget_lengthの文字列があるか判定
def judge(rh: RollingHash, target_length: int, source_length: int):
    memo = {}
    for start_idx in range(source_length - target_length + 1):
        cur_hash = rh.get_hash(start_idx, start_idx + target_length)
        if cur_hash in memo:
            if start_idx - memo[cur_hash] >= target_length:
                return True
        else:
            memo[cur_hash] = start_idx

    return False


n = ni()
s = ns()

rh = RollingHash(s)
print(binsearch(judge, rh, 0, n//2 + 2))

Buggy python submission (verdict: Time Limit Exceeded):
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())

n = ni()
s = lc()

dic = {}
strs = set()

slice = [[""]*n for _ in range(n)]
for st in range(n):
    curstr = s[st]
    for ed in range(st+1, min(st+n//2+2, n)):
        curstr += s[ed]
        slice[st][ed] = curstr

for st in range(n):
    for ed in range(st+1, min(st+n//2+2, n)):
        stri = slice[st][ed]
        if not stri in strs:
            dic[stri] = [st, -1]
            strs.add(stri)
        else:
            dic[stri][1] = st

ans = 0
for key, val in dic.items():
    if val[1] - val[0] >= len(key):
        ans = max(ans, len(key))

print(ans)

Fixed submission:
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())


class RollingHash(object):
    def __init__(self, source: str, base=10**9+7, mod=(1<<64)-1):
        self.source = source
        self.base = base
        self.mod = mod
        self.hash_from_zero_to = self._get_hash_from_zero_to()
        self.base_pow = self._get_base_pow()

    # 文字列sourceの区間 [0, right) のハッシュ値を前計算する
    def _get_hash_from_zero_to(self):
        hash_from_zero_to = [0]
        for si in self.source:
            hash_from_zero_to.append((hash_from_zero_to[-1] * self.base + ord(si)) % self.mod)

        return hash_from_zero_to

    # base値をi回掛けてmodをとった値を前計算する
    def _get_base_pow(self):
        base_pow = [1]
        for i in range(len(self.source)):
            base_pow.append(base_pow[-1] * self.base % self.mod)

        return base_pow

    # 文字列sourceの区間 [left, right) のハッシュを取得する
    def get_hash(self, left: int, right: int):
        return (self.hash_from_zero_to[right]
                - self.hash_from_zero_to[left] * self.base_pow[right - left]) % self.mod


def binsearch(func, rh: RollingHash, ok: int, ng: int):
    source_length = len(rh.source)
    while ng - ok > 1:
        mid = (ok + ng) // 2
        if func(rh, mid, source_length):
            ok = mid
        else:
            ng = mid

    return ok


# 題意を満たす長さtarget_lengthの文字列があるか判定
def judge(rh: RollingHash, target_length: int, source_length: int):
    memo = {}
    for start_idx in range(source_length - target_length + 1):
        cur_hash = rh.get_hash(start_idx, start_idx + target_length)
        if cur_hash in memo:
            if start_idx - memo[cur_hash] >= target_length:
                return True
        else:
            memo[cur_hash] = start_idx

    return False


n = ni()
s = ns()

rh = RollingHash(s)
print(binsearch(judge, rh, 0, n//2 + 2))

Buggy python submission (verdict: Time Limit Exceeded):
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())

from collections import defaultdict
from heapq import heappop, heappush

n = ni()
s = ns()

lenstr = []
dic = defaultdict(list)

for st in range(n):
    for ed in range(st+1, min(st+n//2+2, n+1)):
        heappush(lenstr, (-(ed-st), s[st:ed]))
        dic[s[st:ed]].append(st)

ans = 0

while lenstr:
    leni, stri = heappop(lenstr)
    leni *= -1
    if dic[stri][-1] - dic[stri][0] >= leni:
        ans = max(ans, leni)
        break

print(ans)


Fixed submission:
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())


class RollingHash(object):
    def __init__(self, source: str, base=10**9+7, mod=(1<<64)-1):
        self.source = source
        self.base = base
        self.mod = mod
        self.hash_from_zero_to = self._get_hash_from_zero_to()
        self.base_pow = self._get_base_pow()

    # 文字列sourceの区間 [0, right) のハッシュ値を前計算する
    def _get_hash_from_zero_to(self):
        hash_from_zero_to = [0]
        for si in self.source:
            hash_from_zero_to.append((hash_from_zero_to[-1] * self.base + ord(si)) % self.mod)

        return hash_from_zero_to

    # base値をi回掛けてmodをとった値を前計算する
    def _get_base_pow(self):
        base_pow = [1]
        for i in range(len(self.source)):
            base_pow.append(base_pow[-1] * self.base % self.mod)

        return base_pow

    # 文字列sourceの区間 [left, right) のハッシュを取得する
    def get_hash(self, left: int, right: int):
        return (self.hash_from_zero_to[right]
                - self.hash_from_zero_to[left] * self.base_pow[right - left]) % self.mod


def binsearch(func, rh: RollingHash, ok: int, ng: int):
    source_length = len(rh.source)
    while ng - ok > 1:
        mid = (ok + ng) // 2
        if func(rh, mid, source_length):
            ok = mid
        else:
            ng = mid

    return ok


# 題意を満たす長さtarget_lengthの文字列があるか判定
def judge(rh: RollingHash, target_length: int, source_length: int):
    memo = {}
    for start_idx in range(source_length - target_length + 1):
        cur_hash = rh.get_hash(start_idx, start_idx + target_length)
        if cur_hash in memo:
            if start_idx - memo[cur_hash] >= target_length:
                return True
        else:
            memo[cur_hash] = start_idx

    return False


n = ni()
s = ns()

rh = RollingHash(s)
print(binsearch(judge, rh, 0, n//2 + 2))

Buggy python submission (verdict: Time Limit Exceeded):
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())

from collections import defaultdict

n = ni()
s = ns()

lenstr = []
dic = defaultdict(list)

for st in range(n):
    for ed in range(st+1, min(st+n//2+2, n+1)):
        lenstr.append((ed-st, s[st:ed]))
        dic[s[st:ed]].append(st)

ans = 0

for leni, stri in lenstr:
    if dic[stri][-1] - dic[stri][0] >= leni:
        ans = max(ans, leni)

print(ans)


Fixed submission:
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())


class RollingHash(object):
    def __init__(self, source: str, base=10**9+7, mod=(1<<64)-1):
        self.source = source
        self.base = base
        self.mod = mod
        self.hash_from_zero_to = self._get_hash_from_zero_to()
        self.base_pow = self._get_base_pow()

    # 文字列sourceの区間 [0, right) のハッシュ値を前計算する
    def _get_hash_from_zero_to(self):
        hash_from_zero_to = [0]
        for si in self.source:
            hash_from_zero_to.append((hash_from_zero_to[-1] * self.base + ord(si)) % self.mod)

        return hash_from_zero_to

    # base値をi回掛けてmodをとった値を前計算する
    def _get_base_pow(self):
        base_pow = [1]
        for i in range(len(self.source)):
            base_pow.append(base_pow[-1] * self.base % self.mod)

        return base_pow

    # 文字列sourceの区間 [left, right) のハッシュを取得する
    def get_hash(self, left: int, right: int):
        return (self.hash_from_zero_to[right]
                - self.hash_from_zero_to[left] * self.base_pow[right - left]) % self.mod


def binsearch(func, rh: RollingHash, ok: int, ng: int):
    source_length = len(rh.source)
    while ng - ok > 1:
        mid = (ok + ng) // 2
        if func(rh, mid, source_length):
            ok = mid
        else:
            ng = mid

    return ok


# 題意を満たす長さtarget_lengthの文字列があるか判定
def judge(rh: RollingHash, target_length: int, source_length: int):
    memo = {}
    for start_idx in range(source_length - target_length + 1):
        cur_hash = rh.get_hash(start_idx, start_idx + target_length)
        if cur_hash in memo:
            if start_idx - memo[cur_hash] >= target_length:
                return True
        else:
            memo[cur_hash] = start_idx

    return False


n = ni()
s = ns()

rh = RollingHash(s)
print(binsearch(judge, rh, 0, n//2 + 2))

Buggy python submission (verdict: Time Limit Exceeded):
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())

from collections import defaultdict

n = ni()
s = ns()

lenstr = []
dic = defaultdict(list)

for st in range(n):
    for ed in range(st+1, min(st+n//2+2, n+1)):
        lenstr.append((ed-st, s[st:ed]))
        dic[s[st:ed]].append(st)

ans = 0

for leni, stri in lenstr:
    if dic[stri][-1] - dic[stri][0] >= leni:
        ans = max(ans, leni)

print(ans)


Fixed submission:
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())


class RollingHash(object):
    def __init__(self, source: str, base=10**9+7, mod=(1<<64)-1):
        self.source = source
        self.base = base
        self.mod = mod
        self.hash_from_zero_to = self._get_hash_from_zero_to()
        self.base_pow = self._get_base_pow()

    # 文字列sourceの区間 [0, right) のハッシュ値を前計算する
    def _get_hash_from_zero_to(self):
        hash_from_zero_to = [0]
        for si in self.source:
            hash_from_zero_to.append((hash_from_zero_to[-1] * self.base + ord(si)) % self.mod)

        return hash_from_zero_to

    # base値をi回掛けてmodをとった値を前計算する
    def _get_base_pow(self):
        base_pow = [1]
        for i in range(len(self.source)):
            base_pow.append(base_pow[-1] * self.base % self.mod)

        return base_pow

    # 文字列sourceの区間 [left, right) のハッシュを取得する
    def get_hash(self, left: int, right: int):
        return (self.hash_from_zero_to[right]
                - self.hash_from_zero_to[left] * self.base_pow[right - left]) % self.mod


def binsearch(func, rh: RollingHash, ok: int, ng: int):
    source_length = len(rh.source)
    while ng - ok > 1:
        mid = (ok + ng) // 2
        if func(rh, mid, source_length):
            ok = mid
        else:
            ng = mid

    return ok


# 題意を満たす長さtarget_lengthの文字列があるか判定
def judge(rh: RollingHash, target_length: int, source_length: int):
    memo = {}
    for start_idx in range(source_length - target_length + 1):
        cur_hash = rh.get_hash(start_idx, start_idx + target_length)
        if cur_hash in memo:
            if start_idx - memo[cur_hash] >= target_length:
                return True
        else:
            memo[cur_hash] = start_idx

    return False


n = ni()
s = ns()

rh = RollingHash(s)
print(binsearch(judge, rh, 0, n//2 + 2))

Buggy python submission (verdict: Time Limit Exceeded):
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())

from collections import defaultdict

n = ni()
s = ns()

lenstr = []
dic = defaultdict(list)

for st in range(n):
    for ed in range(st+1, min(st+n//2+2, n+1)):
        lenstr.append((ed-st, s[st:ed]))
        dic[s[st:ed]].append(st)

ans = 0

lenstr.sort(reverse=True)
for leni, stri in lenstr:
    if dic[stri][-1] - dic[stri][0] >= leni:
        ans = leni
        break

print(ans)


Fixed submission:
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())


class RollingHash(object):
    def __init__(self, source: str, base=10**9+7, mod=(1<<64)-1):
        self.source = source
        self.base = base
        self.mod = mod
        self.hash_from_zero_to = self._get_hash_from_zero_to()
        self.base_pow = self._get_base_pow()

    # 文字列sourceの区間 [0, right) のハッシュ値を前計算する
    def _get_hash_from_zero_to(self):
        hash_from_zero_to = [0]
        for si in self.source:
            hash_from_zero_to.append((hash_from_zero_to[-1] * self.base + ord(si)) % self.mod)

        return hash_from_zero_to

    # base値をi回掛けてmodをとった値を前計算する
    def _get_base_pow(self):
        base_pow = [1]
        for i in range(len(self.source)):
            base_pow.append(base_pow[-1] * self.base % self.mod)

        return base_pow

    # 文字列sourceの区間 [left, right) のハッシュを取得する
    def get_hash(self, left: int, right: int):
        return (self.hash_from_zero_to[right]
                - self.hash_from_zero_to[left] * self.base_pow[right - left]) % self.mod


def binsearch(func, rh: RollingHash, ok: int, ng: int):
    source_length = len(rh.source)
    while ng - ok > 1:
        mid = (ok + ng) // 2
        if func(rh, mid, source_length):
            ok = mid
        else:
            ng = mid

    return ok


# 題意を満たす長さtarget_lengthの文字列があるか判定
def judge(rh: RollingHash, target_length: int, source_length: int):
    memo = {}
    for start_idx in range(source_length - target_length + 1):
        cur_hash = rh.get_hash(start_idx, start_idx + target_length)
        if cur_hash in memo:
            if start_idx - memo[cur_hash] >= target_length:
                return True
        else:
            memo[cur_hash] = start_idx

    return False


n = ni()
s = ns()

rh = RollingHash(s)
print(binsearch(judge, rh, 0, n//2 + 2))

Buggy python submission (verdict: Time Limit Exceeded):
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())

from collections import defaultdict

n = ni()
s = ns()

dic = defaultdict(list)

for st in range(n):
    for ed in range(st+1, min(st+n//2+2, n+1)):
        dic[s[st:ed]].append(st)

ans = 0
for stri, stsi in dic.items():
    if len(stsi) <= 1:
        continue

    if stsi[-1] - stsi[0] >= len(stri):
        ans = max(ans, len(stri))

print(ans)

Fixed submission:
import sys
stdin = sys.stdin

sys.setrecursionlimit(10 ** 7)

def li(): return map(int, stdin.readline().split())
def li_(): return map(lambda x: int(x) - 1, stdin.readline().split())
def lf(): return map(float, stdin.readline().split())
def ls(): return stdin.readline().split()
def ns(): return stdin.readline().rstrip()
def lc(): return list(ns())
def ni(): return int(stdin.readline())
def nf(): return float(stdin.readline())


class RollingHash(object):
    def __init__(self, source: str, base=10**9+7, mod=(1<<64)-1):
        self.source = source
        self.base = base
        self.mod = mod
        self.hash_from_zero_to = self._get_hash_from_zero_to()
        self.base_pow = self._get_base_pow()

    # 文字列sourceの区間 [0, right) のハッシュ値を前計算する
    def _get_hash_from_zero_to(self):
        hash_from_zero_to = [0]
        for si in self.source:
            hash_from_zero_to.append((hash_from_zero_to[-1] * self.base + ord(si)) % self.mod)

        return hash_from_zero_to

    # base値をi回掛けてmodをとった値を前計算する
    def _get_base_pow(self):
        base_pow = [1]
        for i in range(len(self.source)):
            base_pow.append(base_pow[-1] * self.base % self.mod)

        return base_pow

    # 文字列sourceの区間 [left, right) のハッシュを取得する
    def get_hash(self, left: int, right: int):
        return (self.hash_from_zero_to[right]
                - self.hash_from_zero_to[left] * self.base_pow[right - left]) % self.mod


def binsearch(func, rh: RollingHash, ok: int, ng: int):
    source_length = len(rh.source)
    while ng - ok > 1:
        mid = (ok + ng) // 2
        if func(rh, mid, source_length):
            ok = mid
        else:
            ng = mid

    return ok


# 題意を満たす長さtarget_lengthの文字列があるか判定
def judge(rh: RollingHash, target_length: int, source_length: int):
    memo = {}
    for start_idx in range(source_length - target_length + 1):
        cur_hash = rh.get_hash(start_idx, start_idx + target_length)
        if cur_hash in memo:
            if start_idx - memo[cur_hash] >= target_length:
                return True
        else:
            memo[cur_hash] = start_idx

    return False


n = ni()
s = ns()

rh = RollingHash(s)
print(binsearch(judge, rh, 0, n//2 + 2))