### BUGGY CODE ###

import random
from collections import defaultdict


class RollingHash():

    def __init__(self, S, b=3491, m=10**9+7):
        rh = 0
        for s in S:
            rh *= b
            rh %= m
            c = self._c(s)
            rh += c
            rh %= m
        size = len(S)
        self.S = S
        self.rh = rh
        self.rh_head = rh
        self.b = b
        self.bn = pow(b, size, m)
        self.binv = pow(b, m-2, m)
        self.m = m
        self.offset = 0
        self.size = size

    def _c(self, s):
        return ord(s) - ord("a") + 1

    def rotate(self):
        """右方向に1字ずらす"""
        if self.size <= 0:
            raise ValueError("No data left.")
        self.rh *= self.b
        self.rh %= self.m
        self.rh -= self._c(self.S[self.offset]) * self.bn
        self.rh %= self.m
        self.rh += self._c(self.S[(self.offset + self.size) % len(self.S)])
        self.rh %= self.m
        self.offset += 1
        self.offset %= len(self.S)
        return self

    def contract(self):
        """右端を1字削る"""
        if self.size <= 0:
            raise ValueError("No data left.")
        self.rh = self.rh_head
        self.rh -= self._c(self.S[self.size-1])
        self.rh %= self.m
        self.rh *= self.binv
        self.rh %= self.m
        self.offset = 0
        self.size -= 1
        self.bn = pow(self.b, self.size, self.m)
        self.rh_head = self.rh
        return self

    def get_hash(self):
        if self.size <= 0:
            raise ValueError("No data left.")
        return self.rh
    
    def __len__(self):
        return self.size

    def __eq__(self, other):
        return self.size == other.size and self.rh == other.rh


def read():
    N = int(input().strip())
    S = input().strip()
    return N, S


def solve(N, S):
    m = 10**9+7
    b = random.randint(10000, m-1)
    rh = RollingHash(S)
    ans = 0
    for n in range(N, N//2, -1):
        rh.contract()
    for n in range(N//2, 0, -1):
        hdict = defaultdict(list)
        for i in range(0, N - n + 1):
            # list all S[i:i+n]
            h = rh.get_hash()
            hdict[h].append(i)
            rh.rotate()
        for h in hdict.keys():
            if hdict[h][-1] - hdict[h][0] > ans:
                ans = max(ans, n)
        rh.contract()
    return ans


if __name__ == '__main__':
    inputs = read()
    outputs = solve(*inputs)
    if outputs is not None:
        print("%s" % str(outputs))

### FIXED CODE ###

import random
from collections import defaultdict


class RollingHash():

    def __init__(self, S, b=3491, m=999999937):
        """任意の基数と法でハッシュを生成する"""
        n = len(S)
        self.prefix = prefix = [0] * (n+1)
        self.power = power = [1] * (n+1)
        self.b = b
        self.m = m
        for i in range(n):
            c = ord(S[i])
            prefix[i+1] = (prefix[i] * b + c) % m
            power[i+1] = (power[i] * b) % m
    
    def get(self, l, r):
        """S[l, r) のハッシュを求める"""
        return (self.prefix[r] - self.power[r-l] * self.prefix[l]) % self.m
    
    def concat(self, h1, h2, l2):
        """S1+S2 のハッシュを、それぞれのハッシュから求める"""
        return (self.power[l2] * h1 + h2) % self.m
    
    def lcp(self, l1, r1, l2, r2):
        """S[l1, r1) とS[l2, r2) の最大共通接頭辞を求める"""
        low = 0
        high = min(r1-l1, r2-l2) + 1
        while high - low > 1:
            mid = (high + low) // 2
            if self.get(l1, l1 + mid) == self.get(l2, l2 + mid):
                low = mid
            else:
                high = mid
        return low


def read():
    N = int(input().strip())
    S = input().strip()
    return N, S


def solve(N, S):
    ans = []
    for m in [999999937, 10**9+7]:
        b = random.randint(10000, m-1)
        rh = RollingHash(S, b=b, m=m)
        low = 0
        high = (N+1) // 2 + 1
        dmax = defaultdict(int)
        dmin = defaultdict(int)
        while high - low > 1:
            dmax.clear()
            dmin.clear()
            mid = (high + low) // 2
            for i in range(0, N-mid+1):
                h = rh.get(i, i+mid)
                dmax[h] = max(dmax[h], i+1)
                dmin[h] = max(dmin[h], N+1-i)
            is_match = False
            for h in dmax.keys():
                if dmax[h] - (N+2 - dmin[h]) >= mid:
                    is_match = True
                    break
            if is_match:
                low = mid
            else:
                high = mid
        ans.append(low)
    return min(ans)


if __name__ == '__main__':
    inputs = read()
    outputs = solve(*inputs)
    if outputs is not None:
        print("%s" % str(outputs))