from collections import defaultdict



class RollingHash(object):

    def __init__(self, S: str, MOD: int = 10 ** 9 + 7, BASE: int = 10 ** 5 + 7):

        self.S = S

        self.N = N = len(S)

        self.MOD = MOD

        self.BASE = BASE

        self.S_arr = [ord(x) for x in S]



        self.POWER = [1] * (N + 1)

        self.HASH  = [0] * (N + 1)

        p, h = 1, 0

        for i in range(N):

            self.POWER[i + 1] = p = (p * BASE) % MOD

            self.HASH[i + 1] = h = (h * BASE + self.S_arr[i]) % MOD



    def hash(self, l: int, r: int):

        # get hash for S[l:r]

        _hash = (self.HASH[r] - self.HASH[l] * self.POWER[r - l]) % self.MOD

        return _hash



def main():

    N = int(eval(input()))

    S = eval(input())

    MOD = 998244353

    rs = RollingHash(S, MOD=MOD, BASE=37)

    h, p = rs.HASH, rs.POWER



    left, right = 0, N // 2 + 1



    while abs(right - left) > 1:

        mid = (left + right) // 2

        ok = False

        hash_to_left = defaultdict(list)

        L = mid

        for i in range(N - L + 1):

            _hash = (h[i+L] - h[i] * p[L]) % MOD

            # hashが衝突しても文字列が完全に一致するか分からないので、

            # 衝突した場合は文字列が完全に一致するか調べ、かつ

            # 文字列同士がSの中で重なるような範囲にない場合はOKとする

            for j in hash_to_left[_hash]:

                if j+L <= i and S[i:i+L] == S[j:j+L]:

                    ok = True

                    break

            if ok:

                break

            hash_to_left[_hash].append(i)



    

        if ok:

            left = mid

        else:

            right = mid



    print(left)

    # # 長さLの部分文字列同士が、範囲を重ねず同じ文字列となる場合はTrue,

    # # そうでない場合はFalseを返す関数

    # def isOK(L):

    #     if L == 0:

    #         return True

    #     hash_to_left = defaultdict(list)

    #     for i in range(N - L + 1):

    #         _hash = rs.hash(i, i + L)

    #         # hashが衝突しても文字列が完全に一致するか分からないので、

    #         # 衝突した場合は文字列が完全に一致するか調べ、かつ

    #         # 文字列同士がSの中で重なるような範囲にない場合はOKとする

    #         for j in hash_to_left[_hash]:

    #             if j+L <= i and S[i:i+L] == S[j:j+L]:

    #                 return True

    #         hash_to_left[_hash].append(i)



    #     return False

    # # 範囲が重ならないことが条件にあるので、答えは最大でもN // 2（文字列の長さの半分）となる。

    # # ただし、二分探索する上で絶対に答えとならない範囲を含めたいので、

    # # 探索範囲としてはN // 2 + 1 と、+1しておく。

    # ans = binary_search(0, N // 2 + 1, isOK, search_max=True)

    # print(ans)



if __name__ == '__main__':

    main()
def solve():

    N = int(eval(input()))

    S = eval(input())



    def rolling_hash(s, mod, base = 37):

        l = len(s)

        h = [0]*(l + 1)

        v = 0

        for i in range(l):

            h[i+1] = v = (v * base + ord(s[i])) % mod

        pw = [1]*(l + 1)

        v = 1

        for i in range(l):

            pw[i+1] = v = v * base % mod

        return h, pw



    def chk(S, mod, D = [0]*N):

        h, pw = rolling_hash(S, mod)

        left = 0; right = N//2+1

        while left+1 < right:

            l = mid = (left + right) >> 1

            p = pw[l]

            for i in range(min(l, N-2*l+1)):

                D[i] = (h[l+i] - h[i]*p) % mod

            s = set()

            ok = 0

            for i in range(l, N-l+1):

                s.add(D[i-l])

                D[i] = v = (h[l+i] - h[i]*p) % mod

                if v in s:

                    ok = 1

                    break

            if ok:

                left = mid

            else:

                right = mid

        return left



    print((chk(S, 10**9 + 9)))

solve()

from collections import defaultdict


class RollingHash(object):
    def __init__(self, S: str, MOD: int = 10**9 + 7, BASE: int = 10**5 + 7):
        self.S = S
        self.N = N = len(S)
        self.MOD = MOD
        self.BASE = BASE
        self.S_arr = [ord(x) for x in S]
        self.POWER = [1] * (N + 1)
        self.HASH = [0] * (N + 1)
        p, h = 1, 0
        for i in range(N):
            self.POWER[i + 1] = p = (p * BASE) % MOD
            self.HASH[i + 1] = h = (h * BASE + self.S_arr[i]) % MOD

    def hash(self, l: int, r: int):
        # get hash for S[l:r]
        _hash = (self.HASH[r] - self.HASH[l] * self.POWER[r - l]) % self.MOD
        return _hash


def main():
    N = int(eval(input()))
    S = eval(input())
    MOD = 998244353
    rs = RollingHash(S, MOD=MOD, BASE=37)
    h, p = rs.HASH, rs.POWER
    left, right = 0, N // 2 + 1
    while abs(right - left) > 1:
        mid = (left + right) // 2
        ok = False
        hash_to_left = defaultdict(list)
        L = mid
        for i in range(N - L + 1):
            _hash = (h[i + L] - h[i] * p[L]) % MOD
            # hashが衝突しても文字列が完全に一致するか分からないので、
            # 衝突した場合は文字列が完全に一致するか調べ、かつ
            # 文字列同士がSの中で重なるような範囲にない場合はOKとする
            for j in hash_to_left[_hash]:
                if j + L <= i and S[i : i + L] == S[j : j + L]:
                    ok = True
                    break
            if ok:
                break
            hash_to_left[_hash].append(i)
        if ok:
            left = mid
        else:
            right = mid
    print(left)
    # # 長さLの部分文字列同士が、範囲を重ねず同じ文字列となる場合はTrue,
    # # そうでない場合はFalseを返す関数
    # def isOK(L):
    #     if L == 0:
    #         return True
    #     hash_to_left = defaultdict(list)
    #     for i in range(N - L + 1):
    #         _hash = rs.hash(i, i + L)
    #         # hashが衝突しても文字列が完全に一致するか分からないので、
    #         # 衝突した場合は文字列が完全に一致するか調べ、かつ
    #         # 文字列同士がSの中で重なるような範囲にない場合はOKとする
    #         for j in hash_to_left[_hash]:
    #             if j+L <= i and S[i:i+L] == S[j:j+L]:
    #                 return True
    #         hash_to_left[_hash].append(i)
    #     return False
    # # 範囲が重ならないことが条件にあるので、答えは最大でもN // 2（文字列の長さの半分）となる。
    # # ただし、二分探索する上で絶対に答えとならない範囲を含めたいので、
    # # 探索範囲としてはN // 2 + 1 と、+1しておく。
    # ans = binary_search(0, N // 2 + 1, isOK, search_max=True)
    # print(ans)


if __name__ == "__main__":
    main()

def solve():
    N = int(eval(input()))
    S = eval(input())

    def rolling_hash(s, mod, base=37):
        l = len(s)
        h = [0] * (l + 1)
        v = 0
        for i in range(l):
            h[i + 1] = v = (v * base + ord(s[i])) % mod
        pw = [1] * (l + 1)
        v = 1
        for i in range(l):
            pw[i + 1] = v = v * base % mod
        return h, pw

    def chk(S, mod, D=[0] * N):
        h, pw = rolling_hash(S, mod)
        left = 0
        right = N // 2 + 1
        while left + 1 < right:
            l = mid = (left + right) >> 1
            p = pw[l]
            for i in range(min(l, N - 2 * l + 1)):
                D[i] = (h[l + i] - h[i] * p) % mod
            s = set()
            ok = 0
            for i in range(l, N - l + 1):
                s.add(D[i - l])
                D[i] = v = (h[l + i] - h[i] * p) % mod
                if v in s:
                    ok = 1
                    break
            if ok:
                left = mid
            else:
                right = mid
        return left

    print((chk(S, 10**9 + 9)))


solve()

-from collections import defaultdict +def solve(): +    N = int(eval(input())) +    S = eval(input()) + +    def rolling_hash(s, mod, base=37): +        l = len(s) +        h = [0] * (l + 1) +        v = 0 +        for i in range(l): +            h[i + 1] = v = (v * base + ord(s[i])) % mod +        pw = [1] * (l + 1) +        v = 1 +        for i in range(l): +            pw[i + 1] = v = v * base % mod +        return h, pw + +    def chk(S, mod, D=[0] * N): +        h, pw = rolling_hash(S, mod) +        left = 0 +        right = N // 2 + 1 +        while left + 1 < right: +            l = mid = (left + right) >> 1 +            p = pw[l] +            for i in range(min(l, N - 2 * l + 1)): +                D[i] = (h[l + i] - h[i] * p) % mod +            s = set() +            ok = 0 +            for i in range(l, N - l + 1): +                s.add(D[i - l]) +                D[i] = v = (h[l + i] - h[i] * p) % mod +                if v in s: +                    ok = 1 +                    break +            if ok: +                left = mid +            else: +                right = mid +        return left + +    print((chk(S, 10**9 + 9))) -class RollingHash(object): -    def __init__(self, S: str, MOD: int = 10**9 + 7, BASE: int = 10**5 + 7): -        self.S = S -        self.N = N = len(S) -        self.MOD = MOD -        self.BASE = BASE -        self.S_arr = [ord(x) for x in S] -        self.POWER = [1] * (N + 1) -        self.HASH = [0] * (N + 1) -        p, h = 1, 0 -        for i in range(N): -            self.POWER[i + 1] = p = (p * BASE) % MOD -            self.HASH[i + 1] = h = (h * BASE + self.S_arr[i]) % MOD - -    def hash(self, l: int, r: int): -        # get hash for S[l:r] -        _hash = (self.HASH[r] - self.HASH[l] * self.POWER[r - l]) % self.MOD -        return _hash - - -def main(): -    N = int(eval(input())) -    S = eval(input()) -    MOD = 998244353 -    rs = RollingHash(S, MOD=MOD, BASE=37) -    h, p = rs.HASH, rs.POWER -    left, right = 0, N // 2 + 1 -    while abs(right - left) > 1: -        mid = (left + right) // 2 -        ok = False -        hash_to_left = defaultdict(list) -        L = mid -        for i in range(N - L + 1): -            _hash = (h[i + L] - h[i] * p[L]) % MOD -            # hashが衝突しても文字列が完全に一致するか分からないので、 -            # 衝突した場合は文字列が完全に一致するか調べ、かつ -            # 文字列同士がSの中で重なるような範囲にない場合はOKとする -            for j in hash_to_left[_hash]: -                if j + L <= i and S[i : i + L] == S[j : j + L]: -                    ok = True -                    break -            if ok: -                break -            hash_to_left[_hash].append(i) -        if ok: -            left = mid -        else: -            right = mid -    print(left) -    # # 長さLの部分文字列同士が、範囲を重ねず同じ文字列となる場合はTrue, -    # # そうでない場合はFalseを返す関数 -    # def isOK(L): -    #     if L == 0: -    #         return True -    #     hash_to_left = defaultdict(list) -    #     for i in range(N - L + 1): -    #         _hash = rs.hash(i, i + L) -    #         # hashが衝突しても文字列が完全に一致するか分からないので、 -    #         # 衝突した場合は文字列が完全に一致するか調べ、かつ -    #         # 文字列同士がSの中で重なるような範囲にない場合はOKとする -    #         for j in hash_to_left[_hash]: -    #             if j+L <= i and S[i:i+L] == S[j:j+L]: -    #                 return True -    #         hash_to_left[_hash].append(i) -    #     return False -    # # 範囲が重ならないことが条件にあるので、答えは最大でもN // 2（文字列の長さの半分）となる。 -    # # ただし、二分探索する上で絶対に答えとならない範囲を含めたいので、 -    # # 探索範囲としてはN // 2 + 1 と、+1しておく。 -    # ans = binary_search(0, N // 2 + 1, isOK, search_max=True) -    # print(ans) - - -if __name__ == "__main__": -    main() +solve()