#ABC141-E Who Says a Pun?
"""
ロリハ解
探索する文字列の長さを二分探索し、重複を探す。
計算量はO((n+m)*log(n//2)) ※mは探索する文字列の長さ
log部分は100には間違いなく収まるので、O(10^5)程度。十分に間に合う。
"""
import sys
readline = sys.stdin.buffer.readline
def even(n): return 1 if n%2==0 else 0

n = int(readline())
s = readline().rstrip().decode('utf-8')

class RollingHash(): #衝突を防ぐため、res1とres2の基数の異なるハッシュによって判定する。
    BASE1 = 1007
    BASE2 = 2009
    MOD1 = 1000000007
    MOD2 = 1000000009
 
    def __init__(self, s):
        self.n = len(s)
        self.hash1 = [0] * (self.n + 1)
        self.hash2 = [0] * (self.n + 1)
        self.power1 = [1] * (self.n + 1)
        self.power2 = [1] * (self.n + 1)
        for i, e in enumerate(s):
            self.hash1[i + 1] = (self.hash1[i] * self.__class__.BASE1 + ord(e)) % self.__class__.MOD1
            self.hash2[i + 1] = (self.hash2[i] * self.__class__.BASE2 + ord(e)) % self.__class__.MOD2
            self.power1[i + 1] = (self.power1[i] * self.__class__.BASE1) % self.__class__.MOD1
            self.power2[i + 1] = (self.power2[i] * self.__class__.BASE2) % self.__class__.MOD2
 
    def get(self, l: int, r: int):
        res1 = self.hash1[r] - self.hash1[l] * self.power1[r - l] % self.__class__.MOD1
        if(res1 < 0):
            res1 += self.__class__.MOD1
        res2 = self.hash2[r] - self.hash2[l] * self.power2[r - l] % self.__class__.MOD2
        if(res2 < 0):
            res2 += self.__class__.MOD2
        return (res1, res2)


def func(mid): #ここが関数部分
    dic1 = dict()

    for i in range(n-mid+1):
        res = roliha.get(i,i+mid)
        if res in dic1:
            #部分重複があってはならない。(2つの文字列は独立)
            if i-dic1[res] >= mid: #先頭文字がmidの幅以上あるならば
                return True
            else:
                continue #更新処理を飛ばさないといけない
        dic1[res] = i
    return False 


def binary_search(): #2分探索
    ok = 0
    ng = n//2+1
    while abs(ok-ng)>1:
        mid = (ok+ng)//2
        if func(mid):
            ok = mid
        else:
            ng = mid

    return ok

roliha = RollingHash(s)

print(binary_search()) """
ロリハ解
探索する文字列の長さを二分探索し、重複を探す。
計算量はO((n+m)*log(n//2)) ※mは探索する文字列の長さ
log部分は100には間違いなく収まるので、O(10^5)程度。十分に間に合う。
""" import sys sys readline = sys.stdin.buffer.readline readline sys.stdin.buffer.readline sys.stdin.buffer sys.stdin sys stdin buffer readline def even(n): return 1 if n%2==0 else 0 even n n n = int(readline()) n int(readline()) int readline() readline s = readline().rstrip().decode('utf-8') s readline().rstrip().decode('utf-8') ().rstrip().decode ().rstrip() ().rstrip () readline rstrip decode 'utf-8' class RollingHash(): #衝突を防ぐため、res1とres2の基数の異なるハッシュによって判定する。
    BASE1 = 1007
    BASE2 = 2009
    MOD1 = 1000000007
    MOD2 = 1000000009
 
    def __init__(self, s):
        self.n = len(s)
        self.hash1 = [0] * (self.n + 1)
        self.hash2 = [0] * (self.n + 1)
        self.power1 = [1] * (self.n + 1)
        self.power2 = [1] * (self.n + 1)
        for i, e in enumerate(s):
            self.hash1[i + 1] = (self.hash1[i] * self.__class__.BASE1 + ord(e)) % self.__class__.MOD1
            self.hash2[i + 1] = (self.hash2[i] * self.__class__.BASE2 + ord(e)) % self.__class__.MOD2
            self.power1[i + 1] = (self.power1[i] * self.__class__.BASE1) % self.__class__.MOD1
            self.power2[i + 1] = (self.power2[i] * self.__class__.BASE2) % self.__class__.MOD2
 
    def get(self, l: int, r: int):
        res1 = self.hash1[r] - self.hash1[l] * self.power1[r - l] % self.__class__.MOD1
        if(res1 < 0):
            res1 += self.__class__.MOD1
        res2 = self.hash2[r] - self.hash2[l] * self.power2[r - l] % self.__class__.MOD2
        if(res2 < 0):
            res2 += self.__class__.MOD2
        return (res1, res2) RollingHash BASE1 = 1007 BASE1 1007 BASE2 = 2009 BASE2 2009 MOD1 = 1000000007 MOD1 1000000007 MOD2 = 1000000009 MOD2 1000000009 def __init__(self, s):
        self.n = len(s)
        self.hash1 = [0] * (self.n + 1)
        self.hash2 = [0] * (self.n + 1)
        self.power1 = [1] * (self.n + 1)
        self.power2 = [1] * (self.n + 1)
        for i, e in enumerate(s):
            self.hash1[i + 1] = (self.hash1[i] * self.__class__.BASE1 + ord(e)) % self.__class__.MOD1
            self.hash2[i + 1] = (self.hash2[i] * self.__class__.BASE2 + ord(e)) % self.__class__.MOD2
            self.power1[i + 1] = (self.power1[i] * self.__class__.BASE1) % self.__class__.MOD1
            self.power2[i + 1] = (self.power2[i] * self.__class__.BASE2) % self.__class__.MOD2
 
     __init__ self.n = len(s) self.n self n len(s) len s self.hash1 = [0] * (self.n + 1) self.hash1 self hash1 [0] * (self.n + 1) [0] 0 self.n + 1 self.n self n 1 self.hash2 = [0] * (self.n + 1) self.hash2 self hash2 [0] * (self.n + 1) [0] 0 self.n + 1 self.n self n 1 self.power1 = [1] * (self.n + 1) self.power1 self power1 [1] * (self.n + 1) [1] 1 self.n + 1 self.n self n 1 self.power2 = [1] * (self.n + 1) self.power2 self power2 [1] * (self.n + 1) [1] 1 self.n + 1 self.n self n 1 for i, e in enumerate(s):
            self.hash1[i + 1] = (self.hash1[i] * self.__class__.BASE1 + ord(e)) % self.__class__.MOD1
            self.hash2[i + 1] = (self.hash2[i] * self.__class__.BASE2 + ord(e)) % self.__class__.MOD2
            self.power1[i + 1] = (self.power1[i] * self.__class__.BASE1) % self.__class__.MOD1
            self.power2[i + 1] = (self.power2[i] * self.__class__.BASE2) % self.__class__.MOD2
 
     i e enumerate(s) enumerate s self.hash1[i + 1] = (self.hash1[i] * self.__class__.BASE1 + ord(e)) % self.__class__.MOD1 self.hash1[i + 1] self.hash1 self hash1 i + 1 i 1 (self.hash1[i] * self.__class__.BASE1 + ord(e)) % self.__class__.MOD1 self.hash1[i] * self.__class__.BASE1 + ord(e) self.hash1[i] * self.__class__.BASE1 self.hash1[i] self.hash1 self hash1 i self.__class__.BASE1 self.__class__ self __class__ BASE1 ord(e) ord e self.__class__.MOD1 self.__class__ self __class__ MOD1 self.hash2[i + 1] = (self.hash2[i] * self.__class__.BASE2 + ord(e)) % self.__class__.MOD2 self.hash2[i + 1] self.hash2 self hash2 i + 1 i 1 (self.hash2[i] * self.__class__.BASE2 + ord(e)) % self.__class__.MOD2 self.hash2[i] * self.__class__.BASE2 + ord(e) self.hash2[i] * self.__class__.BASE2 self.hash2[i] self.hash2 self hash2 i self.__class__.BASE2 self.__class__ self __class__ BASE2 ord(e) ord e self.__class__.MOD2 self.__class__ self __class__ MOD2 self.power1[i + 1] = (self.power1[i] * self.__class__.BASE1) % self.__class__.MOD1 self.power1[i + 1] self.power1 self power1 i + 1 i 1 (self.power1[i] * self.__class__.BASE1) % self.__class__.MOD1 self.power1[i] * self.__class__.BASE1 self.power1[i] self.power1 self power1 i self.__class__.BASE1 self.__class__ self __class__ BASE1 self.__class__.MOD1 self.__class__ self __class__ MOD1 self.power2[i + 1] = (self.power2[i] * self.__class__.BASE2) % self.__class__.MOD2 self.power2[i + 1] self.power2 self power2 i + 1 i 1 (self.power2[i] * self.__class__.BASE2) % self.__class__.MOD2 self.power2[i] * self.__class__.BASE2 self.power2[i] self.power2 self power2 i self.__class__.BASE2 self.__class__ self __class__ BASE2 self.__class__.MOD2 self.__class__ self __class__ MOD2 self self s s def get(self, l: int, r: int):
        res1 = self.hash1[r] - self.hash1[l] * self.power1[r - l] % self.__class__.MOD1
        if(res1 < 0):
            res1 += self.__class__.MOD1
        res2 = self.hash2[r] - self.hash2[l] * self.power2[r - l] % self.__class__.MOD2
        if(res2 < 0):
            res2 += self.__class__.MOD2
        return (res1, res2) get res1 = self.hash1[r] - self.hash1[l] * self.power1[r - l] % self.__class__.MOD1 res1 self.hash1[r] - self.hash1[l] * self.power1[r - l] % self.__class__.MOD1 self.hash1[r] self.hash1 self hash1 r self.hash1[l] * self.power1[r - l] % self.__class__.MOD1 self.hash1[l] * self.power1[r - l] self.hash1[l] self.hash1 self hash1 l self.power1[r - l] self.power1 self power1 r - l r l self.__class__.MOD1 self.__class__ self __class__ MOD1 if(res1 < 0):
            res1 += self.__class__.MOD1
         res1 < 0 res1 0 res1 += self.__class__.MOD1 res1 self.__class__.MOD1 self.__class__ self __class__ MOD1 res2 = self.hash2[r] - self.hash2[l] * self.power2[r - l] % self.__class__.MOD2 res2 self.hash2[r] - self.hash2[l] * self.power2[r - l] % self.__class__.MOD2 self.hash2[r] self.hash2 self hash2 r self.hash2[l] * self.power2[r - l] % self.__class__.MOD2 self.hash2[l] * self.power2[r - l] self.hash2[l] self.hash2 self hash2 l self.power2[r - l] self.power2 self power2 r - l r l self.__class__.MOD2 self.__class__ self __class__ MOD2 if(res2 < 0):
            res2 += self.__class__.MOD2
         res2 < 0 res2 0 res2 += self.__class__.MOD2 res2 self.__class__.MOD2 self.__class__ self __class__ MOD2 return (res1, res2) (res1, res2) res1 res2 self self l: int l r: int r def func(mid): #ここが関数部分
    dic1 = dict()

    for i in range(n-mid+1):
        res = roliha.get(i,i+mid)
        if res in dic1:
            #部分重複があってはならない。(2つの文字列は独立)
            if i-dic1[res] >= mid: #先頭文字がmidの幅以上あるならば
                return True
            else:
                continue #更新処理を飛ばさないといけない
        dic1[res] = i
    return False  func dic1 = dict() dic1 dict() dict for i in range(n-mid+1):
        res = roliha.get(i,i+mid)
        if res in dic1:
            #部分重複があってはならない。(2つの文字列は独立)
            if i-dic1[res] >= mid: #先頭文字がmidの幅以上あるならば
                return True
            else:
                continue #更新処理を飛ばさないといけない
        dic1[res] = i
     i range(n-mid+1) range n-mid+1 n-mid n mid 1 res = roliha.get(i,i+mid) res roliha.get(i,i+mid) roliha.get roliha get i i+mid i mid if res in dic1:
            #部分重複があってはならない。(2つの文字列は独立)
            if i-dic1[res] >= mid: #先頭文字がmidの幅以上あるならば
                return True
            else:
                continue #更新処理を飛ばさないといけない
         res in dic1 res dic1 if i-dic1[res] >= mid: #先頭文字がmidの幅以上あるならば
                return True
            else:
                continue #更新処理を飛ばさないといけない
         i-dic1[res] >= mid i-dic1[res] i dic1[res] dic1 res mid return True True continue dic1[res] = i dic1[res] dic1 res i return False False mid mid def binary_search(): #2分探索
    ok = 0
    ng = n//2+1
    while abs(ok-ng)>1:
        mid = (ok+ng)//2
        if func(mid):
            ok = mid
        else:
            ng = mid

    return ok binary_search ok = 0 ok 0 ng = n//2+1 ng n//2+1 n//2 n 2 1 while abs(ok-ng)>1:
        mid = (ok+ng)//2
        if func(mid):
            ok = mid
        else:
            ng = mid

     abs(ok-ng)>1 abs(ok-ng) abs ok-ng ok ng 1 mid = (ok+ng)//2 mid (ok+ng)//2 ok+ng ok ng 2 if func(mid):
            ok = mid
        else:
            ng = mid

     func(mid) func mid ok = mid ok mid ng = mid ng mid return ok ok roliha = RollingHash(s) roliha RollingHash(s) RollingHash s print(binary_search()) print binary_search() binary_search s = readline().rstrip().decode('utf-8') readline().rstrip().decode('utf-8') s n = int(readline()) int(readline()) n class RollingHash(): #衝突を防ぐため、res1とres2の基数の異なるハッシュによって判定する。
    BASE1 = 1007
    BASE2 = 2009
    MOD1 = 1000000007
    MOD2 = 1000000009
 
    def __init__(self, s):
        self.n = len(s)
        self.hash1 = [0] * (self.n + 1)
        self.hash2 = [0] * (self.n + 1)
        self.power1 = [1] * (self.n + 1)
        self.power2 = [1] * (self.n + 1)
        for i, e in enumerate(s):
            self.hash1[i + 1] = (self.hash1[i] * self.__class__.BASE1 + ord(e)) % self.__class__.MOD1
            self.hash2[i + 1] = (self.hash2[i] * self.__class__.BASE2 + ord(e)) % self.__class__.MOD2
            self.power1[i + 1] = (self.power1[i] * self.__class__.BASE1) % self.__class__.MOD1
            self.power2[i + 1] = (self.power2[i] * self.__class__.BASE2) % self.__class__.MOD2
 
    def get(self, l: int, r: int):
        res1 = self.hash1[r] - self.hash1[l] * self.power1[r - l] % self.__class__.MOD1
        if(res1 < 0):
            res1 += self.__class__.MOD1
        res2 = self.hash2[r] - self.hash2[l] * self.power2[r - l] % self.__class__.MOD2
        if(res2 < 0):
            res2 += self.__class__.MOD2
        return (res1, res2) class RollingHash(): #衝突を防ぐため、res1とres2の基数の異なるハッシュによって判定する。
    BASE1 = 1007
    BASE2 = 2009
    MOD1 = 1000000007
    MOD2 = 1000000009
 
    def __init__(self, s):
        self.n = len(s)
        self.hash1 = [0] * (self.n + 1)
        self.hash2 = [0] * (self.n + 1)
        self.power1 = [1] * (self.n + 1)
        self.power2 = [1] * (self.n + 1)
        for i, e in enumerate(s):
            self.hash1[i + 1] = (self.hash1[i] * self.__class__.BASE1 + ord(e)) % self.__class__.MOD1
            self.hash2[i + 1] = (self.hash2[i] * self.__class__.BASE2 + ord(e)) % self.__class__.MOD2
            self.power1[i + 1] = (self.power1[i] * self.__class__.BASE1) % self.__class__.MOD1
            self.power2[i + 1] = (self.power2[i] * self.__class__.BASE2) % self.__class__.MOD2
 
    def get(self, l: int, r: int):
        res1 = self.hash1[r] - self.hash1[l] * self.power1[r - l] % self.__class__.MOD1
        if(res1 < 0):
            res1 += self.__class__.MOD1
        res2 = self.hash2[r] - self.hash2[l] * self.power2[r - l] % self.__class__.MOD2
        if(res2 < 0):
            res2 += self.__class__.MOD2
        return (res1, res2) RollingHash def func(mid): #ここが関数部分
    dic1 = dict()

    for i in range(n-mid+1):
        res = roliha.get(i,i+mid)
        if res in dic1:
            #部分重複があってはならない。(2つの文字列は独立)
            if i-dic1[res] >= mid: #先頭文字がmidの幅以上あるならば
                return True
            else:
                continue #更新処理を飛ばさないといけない
        dic1[res] = i
    return False  def func(mid): #ここが関数部分
    dic1 = dict()

    for i in range(n-mid+1):
        res = roliha.get(i,i+mid)
        if res in dic1:
            #部分重複があってはならない。(2つの文字列は独立)
            if i-dic1[res] >= mid: #先頭文字がmidの幅以上あるならば
                return True
            else:
                continue #更新処理を飛ばさないといけない
        dic1[res] = i
    return False  func roliha = RollingHash(s) RollingHash(s) roliha def even(n): return 1 if n%2==0 else 0 def even(n): return 1 if n%2==0 else 0 even def binary_search(): #2分探索
    ok = 0
    ng = n//2+1
    while abs(ok-ng)>1:
        mid = (ok+ng)//2
        if func(mid):
            ok = mid
        else:
            ng = mid

    return ok def binary_search(): #2分探索
    ok = 0
    ng = n//2+1
    while abs(ok-ng)>1:
        mid = (ok+ng)//2
        if func(mid):
            ok = mid
        else:
            ng = mid

    return ok binary_search readline = sys.stdin.buffer.readline sys.stdin.buffer.readline readline
#ABC141-E Who Says a Pun?
"""
ロリハ解
探索する文字列の長さを二分探索し、重複を探す。
計算量はO((n+m)*log(n//2)) ※mは探索する文字列の長さ
log部分は100には間違いなく収まるので、O(10^5)程度。十分に間に合う。
"""
import sys
readline = sys.stdin.buffer.readline
def even(n): return 1 if n%2==0 else 0

n = int(readline())
s = readline().rstrip().decode('utf-8')

class RollingHash(): #衝突を防ぐため、res1とres2の基数の異なるハッシュによって判定する。
    BASE1 = 1007
    BASE2 = 2009
    MOD1 = 1000000007
    MOD2 = 1000000009
 
    def __init__(self, s):
        self.n = len(s)
        self.hash1 = [0] * (self.n + 1)
        self.hash2 = [0] * (self.n + 1)
        self.power1 = [1] * (self.n + 1)
        self.power2 = [1] * (self.n + 1)
        for i, e in enumerate(s):
            self.hash1[i + 1] = (self.hash1[i] * self.__class__.BASE1 + ord(e)) % self.__class__.MOD1
            self.hash2[i + 1] = (self.hash2[i] * self.__class__.BASE2 + ord(e)) % self.__class__.MOD2
            self.power1[i + 1] = (self.power1[i] * self.__class__.BASE1) % self.__class__.MOD1
            self.power2[i + 1] = (self.power2[i] * self.__class__.BASE2) % self.__class__.MOD2
 
    def get(self, l: int, r: int):
        res1 = self.hash1[r] - self.hash1[l] * self.power1[r - l] % self.__class__.MOD1
        if(res1 < 0):
            res1 += self.__class__.MOD1
        res2 = self.hash2[r] - self.hash2[l] * self.power2[r - l] % self.__class__.MOD2
        if(res2 < 0):
            res2 += self.__class__.MOD2
        return (res1, res2)


def func(mid): #ここが関数部分
    dic1 = dict()

    for i in range(n-mid+1):
        res = roliha.get(i,i+mid)
        if res in dic1:
            #部分重複があってはならない。(2つの文字列は独立)
            if i-dic1[res] >= mid: #先頭文字がmidの幅以上あるならば
                return True
            else:
                continue #更新処理を飛ばさないといけない
        dic1[res] = i
    return False 


def binary_search(): #2分探索
    ok = 0
    ng = n//2+1
    while abs(ok-ng)>1:
        mid = (ok+ng)//2
        if func(mid):
            ok = mid
        else:
            ng = mid

    return ok

roliha = RollingHash(s)

print(binary_search())