import sys

sys.setrecursionlimit(2147483647)

INF=float("inf")

MOD=10**9+7

input=lambda :sys.stdin.readline().rstrip()

class SuffixArray(object):

    """

    construct:

        suffix array: O(N(logN)^2)

        lcp array: O(N)

        sparse table: O(NlogN)

    query:

        lcp: O(1)

    """

    def __init__(self,s):

        """

        s: str

        """

        self.__s=s

        self.__n=len(s)

        self.__suffix_array()

        self.__lcp_array()

        self.__sparse_table()



    # suffix array

    def __suffix_array(self):

        s=self.__s; n=self.__n

        # initialize

        sa=list(range(n))

        rank=[ord(s[i]) for i in range(n)]

        tmp=[0]*n

        k=1

        cmp_key=lambda i:(rank[i],rank[i+k] if i+k<n else -1)

        # iterate

        while(k<=n):

            sa.sort(key=cmp_key)

            tmp[sa[0]]=0

            for i in range(1,n):

                tmp[sa[i]]=tmp[sa[i-1]]+(cmp_key(sa[i-1])<cmp_key(sa[i]))

            rank=tmp[:]

            k<<=1

        self.__sa=sa

        self.__rank=rank



    # LCP array

    def __lcp_array(self):

        s=self.__s; n=self.__n

        sa=self.__sa; rank=self.__rank

        lcp=[0]*n

        h=0

        for i in range(n):

            j=sa[rank[i]-1]

            if h>0: h-=1

            while j+h<n and i+h<n and s[j+h]==s[i+h]: h+=1

            lcp[rank[i]]=h

        self.__lcp=lcp



    # sparse table

    def __sparse_table(self):

        n=self.__n

        logn=max(0,(n-1).bit_length())

        table=[[0]*n for _ in range(logn)]

        table[0]=self.__lcp[:]

        # construct

        from itertools import product

        for i,k in product(list(range(1,logn)),list(range(n))):

            if k+(1<<(i-1))>=n:

                table[i][k]=table[i-1][k]

            else:

                table[i][k]=min(table[i-1][k],table[i-1][k+(1<<(i-1))])

        self.__table=table



    def lcp(self,a,b):

        """

        a,b: int 0<=a,b<n

        return LCP length between s[a:] and s[b:]

        """

        if a==b: return self.__n-a

        l,r=self.__rank[a],self.__rank[b]

        l,r=min(l,r)+1,max(l,r)+1

        i=max(0,(r-l-1).bit_length()-1)

        table=self.__table

        return min(table[i][l],table[i][r-(1<<i)])

def resolve():

    n=int(eval(input()))

    s=eval(input())

    sa=SuffixArray(s)

    ans=0

    for i in range(n):

        for j in range(i+1,n):

            ans=max(ans,min(sa.lcp(i,j),j-i))

    print(ans)

resolve()
import sys

sys.setrecursionlimit(2147483647)

INF=float("inf")

MOD=10**9+7

input=lambda :sys.stdin.readline().rstrip()

def bisect(l,r,f,discrete=True,left=True):

    """

    l,r: l<r int if discrete else float

    f: function defined on [l,...,r] to {False,True}

    if discrete: f defined on Z; else: R

    if left: f satisfies that there uniquely exists d such that iff i<=d then f(i)

    else: iff i>=d then f(i) is True

    return d such as those above

    """

    assert r>l

    if discrete: assert isinstance(l,int) and isinstance(r,int)

    eps=1 if discrete else 10**-12

    if (not left)^f(r): return r if left else r+1

    elif left^f(l): return l-1 if left else l

    while(r-l>eps):

        h=(l+r)//2 if discrete else (l+r)/2

        if (not left)^f(h): l=h

        else: r=h

    return h if not discrete else l if left else r

    

class RollingHash(object):

    """

    construct: O(N)

    query:

        hash: O(1)

        lcp: O(logN)

    """

    __base1=1007; __mod1=10**9

    __base2=1009; __mod2=10**7



    def __init__(self,S):

        """

        S: str

        """

        n=len(S)

        self.__n=n

        b1=self.__base1; m1=self.__mod1

        b2=self.__base2; m2=self.__mod2

        H1,H2=[0]*(n+1),[0]*(n+1)

        P1,P2=[1]*(n+1),[1]*(n+1)

        for i,s in enumerate(S):

            H1[i+1]=(H1[i]*b1+ord(s))%m1

            H2[i+1]=(H2[i]*b2+ord(s))%m2

            P1[i+1]=P1[i]*b1%m1

            P2[i+1]=P2[i]*b2%m2

        self.__H1=H1; self.__H2=H2

        self.__P1=P1; self.__P2=P2



    @property

    def len(self):

        return self.__n



    def hash(self,l,r=None):

        """

        l,r: int (0<=l<=r<=n)

        return (hash1,hash2) of S[l:r]

        """

        m1=self.__mod1; m2=self.__mod2

        if r is None: r=self.__n

        assert 0<=l<=r<=self.__n

        return ((self.__H1[r]-self.__P1[r-l]*self.__H1[l]%m1)%m1,

                (self.__H2[r]-self.__P2[r-l]*self.__H2[l]%m2)%m2)



    @classmethod

    def lcp(cls,rh1,rh2,l1,l2,r1=None,r2=None):

        """

        rh1,rh2: RollingHash object

        l1,l2,r1,r2: int 0<=l1<=r1<=r1.len,0<=l2<=r2<=rh2.len

        return lcp length between rh1[l1:r1] and rh2[l2:r2]

        """

        if r1 is None: r1=rh1.__n

        if r2 is None: r2=rh2.__n

        assert 0<=l1<=r1<=rh1.__n and 0<=l2<=r2<=rh2.__n

        L=0

        R=min(r1-l1,r2-l2)

        if rh1.hash(l1,l1+R)==rh2.hash(l2,l2+R): return R

        while(R-L>1):

            H=(L+R)//2

            if rh1.hash(l1,l1+H)==rh2.hash(l2,l2+H): L=H

            else: R=H

        return L



def resolve():

    n=int(eval(input()))

    S=eval(input())

    # d=0ではtrue,d=nではfalseなので、そこでbisectする

    rh=RollingHash(S)

    def judge(d):

        # initialize

        J={rh.hash(i,i+d):-1 for i in range(n-d+1)}

        for i in range(n-d+1):

            h=rh.hash(i,i+d)

            if J[h]!=-1:

                if i-J[h]>=d: return True

            else: J[h]=i

        return False

    print((bisect(0,n,judge)))

resolve()
import sys

sys.setrecursionlimit(2147483647)
INF = float("inf")
MOD = 10**9 + 7
input = lambda: sys.stdin.readline().rstrip()


class SuffixArray(object):
    """
    construct:
        suffix array: O(N(logN)^2)
        lcp array: O(N)
        sparse table: O(NlogN)
    query:
        lcp: O(1)
    """

    def __init__(self, s):
        """
        s: str
        """
        self.__s = s
        self.__n = len(s)
        self.__suffix_array()
        self.__lcp_array()
        self.__sparse_table()

    # suffix array
    def __suffix_array(self):
        s = self.__s
        n = self.__n
        # initialize
        sa = list(range(n))
        rank = [ord(s[i]) for i in range(n)]
        tmp = [0] * n
        k = 1
        cmp_key = lambda i: (rank[i], rank[i + k] if i + k < n else -1)
        # iterate
        while k <= n:
            sa.sort(key=cmp_key)
            tmp[sa[0]] = 0
            for i in range(1, n):
                tmp[sa[i]] = tmp[sa[i - 1]] + (cmp_key(sa[i - 1]) < cmp_key(sa[i]))
            rank = tmp[:]
            k <<= 1
        self.__sa = sa
        self.__rank = rank

    # LCP array
    def __lcp_array(self):
        s = self.__s
        n = self.__n
        sa = self.__sa
        rank = self.__rank
        lcp = [0] * n
        h = 0
        for i in range(n):
            j = sa[rank[i] - 1]
            if h > 0:
                h -= 1
            while j + h < n and i + h < n and s[j + h] == s[i + h]:
                h += 1
            lcp[rank[i]] = h
        self.__lcp = lcp

    # sparse table
    def __sparse_table(self):
        n = self.__n
        logn = max(0, (n - 1).bit_length())
        table = [[0] * n for _ in range(logn)]
        table[0] = self.__lcp[:]
        # construct
        from itertools import product

        for i, k in product(list(range(1, logn)), list(range(n))):
            if k + (1 << (i - 1)) >= n:
                table[i][k] = table[i - 1][k]
            else:
                table[i][k] = min(table[i - 1][k], table[i - 1][k + (1 << (i - 1))])
        self.__table = table

    def lcp(self, a, b):
        """
        a,b: int 0<=a,b<n
        return LCP length between s[a:] and s[b:]
        """
        if a == b:
            return self.__n - a
        l, r = self.__rank[a], self.__rank[b]
        l, r = min(l, r) + 1, max(l, r) + 1
        i = max(0, (r - l - 1).bit_length() - 1)
        table = self.__table
        return min(table[i][l], table[i][r - (1 << i)])


def resolve():
    n = int(eval(input()))
    s = eval(input())
    sa = SuffixArray(s)
    ans = 0
    for i in range(n):
        for j in range(i + 1, n):
            ans = max(ans, min(sa.lcp(i, j), j - i))
    print(ans)


resolve()

import sys

sys.setrecursionlimit(2147483647)
INF = float("inf")
MOD = 10**9 + 7
input = lambda: sys.stdin.readline().rstrip()


def bisect(l, r, f, discrete=True, left=True):
    """
    l,r: l<r int if discrete else float
    f: function defined on [l,...,r] to {False,True}
    if discrete: f defined on Z; else: R
    if left: f satisfies that there uniquely exists d such that iff i<=d then f(i)
    else: iff i>=d then f(i) is True
    return d such as those above
    """
    assert r > l
    if discrete:
        assert isinstance(l, int) and isinstance(r, int)
    eps = 1 if discrete else 10**-12
    if (not left) ^ f(r):
        return r if left else r + 1
    elif left ^ f(l):
        return l - 1 if left else l
    while r - l > eps:
        h = (l + r) // 2 if discrete else (l + r) / 2
        if (not left) ^ f(h):
            l = h
        else:
            r = h
    return h if not discrete else l if left else r


class RollingHash(object):
    """
    construct: O(N)
    query:
        hash: O(1)
        lcp: O(logN)
    """

    __base1 = 1007
    __mod1 = 10**9
    __base2 = 1009
    __mod2 = 10**7

    def __init__(self, S):
        """
        S: str
        """
        n = len(S)
        self.__n = n
        b1 = self.__base1
        m1 = self.__mod1
        b2 = self.__base2
        m2 = self.__mod2
        H1, H2 = [0] * (n + 1), [0] * (n + 1)
        P1, P2 = [1] * (n + 1), [1] * (n + 1)
        for i, s in enumerate(S):
            H1[i + 1] = (H1[i] * b1 + ord(s)) % m1
            H2[i + 1] = (H2[i] * b2 + ord(s)) % m2
            P1[i + 1] = P1[i] * b1 % m1
            P2[i + 1] = P2[i] * b2 % m2
        self.__H1 = H1
        self.__H2 = H2
        self.__P1 = P1
        self.__P2 = P2

    @property
    def len(self):
        return self.__n

    def hash(self, l, r=None):
        """
        l,r: int (0<=l<=r<=n)
        return (hash1,hash2) of S[l:r]
        """
        m1 = self.__mod1
        m2 = self.__mod2
        if r is None:
            r = self.__n
        assert 0 <= l <= r <= self.__n
        return (
            (self.__H1[r] - self.__P1[r - l] * self.__H1[l] % m1) % m1,
            (self.__H2[r] - self.__P2[r - l] * self.__H2[l] % m2) % m2,
        )

    @classmethod
    def lcp(cls, rh1, rh2, l1, l2, r1=None, r2=None):
        """
        rh1,rh2: RollingHash object
        l1,l2,r1,r2: int 0<=l1<=r1<=r1.len,0<=l2<=r2<=rh2.len
        return lcp length between rh1[l1:r1] and rh2[l2:r2]
        """
        if r1 is None:
            r1 = rh1.__n
        if r2 is None:
            r2 = rh2.__n
        assert 0 <= l1 <= r1 <= rh1.__n and 0 <= l2 <= r2 <= rh2.__n
        L = 0
        R = min(r1 - l1, r2 - l2)
        if rh1.hash(l1, l1 + R) == rh2.hash(l2, l2 + R):
            return R
        while R - L > 1:
            H = (L + R) // 2
            if rh1.hash(l1, l1 + H) == rh2.hash(l2, l2 + H):
                L = H
            else:
                R = H
        return L


def resolve():
    n = int(eval(input()))
    S = eval(input())
    # d=0ではtrue,d=nではfalseなので、そこでbisectする
    rh = RollingHash(S)

    def judge(d):
        # initialize
        J = {rh.hash(i, i + d): -1 for i in range(n - d + 1)}
        for i in range(n - d + 1):
            h = rh.hash(i, i + d)
            if J[h] != -1:
                if i - J[h] >= d:
                    return True
            else:
                J[h] = i
        return False

    print((bisect(0, n, judge)))


resolve()

-class SuffixArray(object): +def bisect(l, r, f, discrete=True, left=True): -    construct: -        suffix array: O(N(logN)^2) -        lcp array: O(N) -        sparse table: O(NlogN) +    l,r: l<r int if discrete else float +    f: function defined on [l,...,r] to {False,True} +    if discrete: f defined on Z; else: R +    if left: f satisfies that there uniquely exists d such that iff i<=d then f(i) +    else: iff i>=d then f(i) is True +    return d such as those above +    """ +    assert r > l +    if discrete: +        assert isinstance(l, int) and isinstance(r, int) +    eps = 1 if discrete else 10**-12 +    if (not left) ^ f(r): +        return r if left else r + 1 +    elif left ^ f(l): +        return l - 1 if left else l +    while r - l > eps: +        h = (l + r) // 2 if discrete else (l + r) / 2 +        if (not left) ^ f(h): +            l = h +        else: +            r = h +    return h if not discrete else l if left else r + + +class RollingHash(object): +    """ +    construct: O(N) -        lcp: O(1) +        hash: O(1) +        lcp: O(logN) -    def __init__(self, s): +    __base1 = 1007 +    __mod1 = 10**9 +    __base2 = 1009 +    __mod2 = 10**7 + +    def __init__(self, S): -        s: str +        S: str -        self.__s = s -        self.__n = len(s) -        self.__suffix_array() -        self.__lcp_array() -        self.__sparse_table() +        n = len(S) +        self.__n = n +        b1 = self.__base1 +        m1 = self.__mod1 +        b2 = self.__base2 +        m2 = self.__mod2 +        H1, H2 = [0] * (n + 1), [0] * (n + 1) +        P1, P2 = [1] * (n + 1), [1] * (n + 1) +        for i, s in enumerate(S): +            H1[i + 1] = (H1[i] * b1 + ord(s)) % m1 +            H2[i + 1] = (H2[i] * b2 + ord(s)) % m2 +            P1[i + 1] = P1[i] * b1 % m1 +            P2[i + 1] = P2[i] * b2 % m2 +        self.__H1 = H1 +        self.__H2 = H2 +        self.__P1 = P1 +        self.__P2 = P2 -    # suffix array -    def __suffix_array(self): -        s = self.__s -        n = self.__n -        # initialize -        sa = list(range(n)) -        rank = [ord(s[i]) for i in range(n)] -        tmp = [0] * n -        k = 1 -        cmp_key = lambda i: (rank[i], rank[i + k] if i + k < n else -1) -        # iterate -        while k <= n: -            sa.sort(key=cmp_key) -            tmp[sa[0]] = 0 -            for i in range(1, n): -                tmp[sa[i]] = tmp[sa[i - 1]] + (cmp_key(sa[i - 1]) < cmp_key(sa[i])) -            rank = tmp[:] -            k <<= 1 -        self.__sa = sa -        self.__rank = rank +    @property +    def len(self): +        return self.__n -    # LCP array -    def __lcp_array(self): -        s = self.__s -        n = self.__n -        sa = self.__sa -        rank = self.__rank -        lcp = [0] * n -        h = 0 -        for i in range(n): -            j = sa[rank[i] - 1] -            if h > 0: -                h -= 1 -            while j + h < n and i + h < n and s[j + h] == s[i + h]: -                h += 1 -            lcp[rank[i]] = h -        self.__lcp = lcp +    def hash(self, l, r=None): +        """ +        l,r: int (0<=l<=r<=n) +        return (hash1,hash2) of S[l:r] +        """ +        m1 = self.__mod1 +        m2 = self.__mod2 +        if r is None: +            r = self.__n +        assert 0 <= l <= r <= self.__n +        return ( +            (self.__H1[r] - self.__P1[r - l] * self.__H1[l] % m1) % m1, +            (self.__H2[r] - self.__P2[r - l] * self.__H2[l] % m2) % m2, +        ) -    # sparse table -    def __sparse_table(self): -        n = self.__n -        logn = max(0, (n - 1).bit_length()) -        table = [[0] * n for _ in range(logn)] -        table[0] = self.__lcp[:] -        # construct -        from itertools import product - -        for i, k in product(list(range(1, logn)), list(range(n))): -            if k + (1 << (i - 1)) >= n: -                table[i][k] = table[i - 1][k] +    @classmethod +    def lcp(cls, rh1, rh2, l1, l2, r1=None, r2=None): +        """ +        rh1,rh2: RollingHash object +        l1,l2,r1,r2: int 0<=l1<=r1<=r1.len,0<=l2<=r2<=rh2.len +        return lcp length between rh1[l1:r1] and rh2[l2:r2] +        """ +        if r1 is None: +            r1 = rh1.__n +        if r2 is None: +            r2 = rh2.__n +        assert 0 <= l1 <= r1 <= rh1.__n and 0 <= l2 <= r2 <= rh2.__n +        L = 0 +        R = min(r1 - l1, r2 - l2) +        if rh1.hash(l1, l1 + R) == rh2.hash(l2, l2 + R): +            return R +        while R - L > 1: +            H = (L + R) // 2 +            if rh1.hash(l1, l1 + H) == rh2.hash(l2, l2 + H): +                L = H -                table[i][k] = min(table[i - 1][k], table[i - 1][k + (1 << (i - 1))]) -        self.__table = table - -    def lcp(self, a, b): -        """ -        a,b: int 0<=a,b<n -        return LCP length between s[a:] and s[b:] -        """ -        if a == b: -            return self.__n - a -        l, r = self.__rank[a], self.__rank[b] -        l, r = min(l, r) + 1, max(l, r) + 1 -        i = max(0, (r - l - 1).bit_length() - 1) -        table = self.__table -        return min(table[i][l], table[i][r - (1 << i)]) +                R = H +        return L -    s = eval(input()) -    sa = SuffixArray(s) -    ans = 0 -    for i in range(n): -        for j in range(i + 1, n): -            ans = max(ans, min(sa.lcp(i, j), j - i)) -    print(ans) +    S = eval(input()) +    # d=0ではtrue,d=nではfalseなので、そこでbisectする +    rh = RollingHash(S) + +    def judge(d): +        # initialize +        J = {rh.hash(i, i + d): -1 for i in range(n - d + 1)} +        for i in range(n - d + 1): +            h = rh.hash(i, i + d) +            if J[h] != -1: +                if i - J[h] >= d: +                    return True +            else: +                J[h] = i +        return False + +    print((bisect(0, n, judge)))