結果

問題 No.2454 Former < Latter
ユーザー FromBooskaFromBooska
提出日時 2023-09-02 13:44:45
言語 PyPy3
(7.3.15)
結果
AC  
実行時間 431 ms / 2,000 ms
コード長 6,944 bytes
コンパイル時間 286 ms
コンパイル使用メモリ 82,048 KB
実行使用メモリ 78,852 KB
最終ジャッジ日時 2024-06-11 20:23:07
合計ジャッジ時間 3,422 ms
ジャッジサーバーID
(参考情報)
judge2 / judge4
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 36 ms
53,120 KB
testcase_01 AC 36 ms
53,120 KB
testcase_02 AC 76 ms
78,840 KB
testcase_03 AC 66 ms
78,208 KB
testcase_04 AC 74 ms
78,720 KB
testcase_05 AC 71 ms
78,720 KB
testcase_06 AC 66 ms
78,336 KB
testcase_07 AC 74 ms
78,592 KB
testcase_08 AC 99 ms
76,768 KB
testcase_09 AC 77 ms
77,568 KB
testcase_10 AC 88 ms
77,500 KB
testcase_11 AC 66 ms
78,592 KB
testcase_12 AC 65 ms
78,592 KB
testcase_13 AC 64 ms
78,208 KB
testcase_14 AC 65 ms
78,396 KB
testcase_15 AC 73 ms
78,248 KB
testcase_16 AC 75 ms
78,512 KB
testcase_17 AC 431 ms
77,340 KB
testcase_18 AC 101 ms
77,312 KB
testcase_19 AC 66 ms
78,464 KB
testcase_20 AC 81 ms
78,852 KB
testcase_21 AC 107 ms
76,600 KB
testcase_22 AC 95 ms
77,524 KB
testcase_23 AC 86 ms
77,892 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

# 公式解説よりZ algorithm

# suffix array
# https://qiita.com/flare/items/20439a1db54b367eea70
# stingは文字列すべて、suffixは接尾辞、prefixは接頭辞
# たとえばstring=bananaのとき、
# suffixは、banana, anana, nana, ana, na, a
# prefixは、banana, banan, bana, ban, ba, b
# suffix arrayは文字列のすべてのsuffixを辞書順でソートし,その開始位置を保持した配列
# 文字列T中に出現する部分文字列は,その出現位置を開始位置とするTのsuffixのprefix
# よって、文字列にあるパターンが存在するかどうかのチェックは、
# パターンPをprefixとしてもつようなTのsuffixの探索となる
# たとえば、S = 'abca' のsuffix array
# (index, suffix) = (0, abca), (1, bca), (2, ca), (3, a)
# 辞書順ソートして、 (3, a), (0, abca), (1, bca), (2, ca)
# suffix array = [3, 0, 1, 2]

# LCP array, Longest Common Prefix array
# suffix arrayの隣同士の文字列のprefix共通文字数の配列、つまり文字数-1の長さになる
# たとえばS = 'banana'
# suffix array = (5, a), (3, ana), (1, anana), (0, banana), (4, na), (2, nana)
# LCP array = (1 for a), (3 for ana), (0 for none), (0 for none), (2 for na)
# LCP array = [1, 3, 0, 0, 2]
# https://blog.shibayu36.org/entry/2017/01/06/103956

# Zアルゴリズム
# 「文字列Sを0番目からとi番目から同時に見ていった時の文字一致数」を記録した配列
# たとえば文字列momomosumomosu (桃もスモモ酢)
# 0字目からと0字目からの最大一致数は全数である14
# 0字目からと1字目から(omomo---)の最大一致数は0
# 0字目からと2字目から(momo---)の最大一致数は4
# 0字目からと3字目から(omosu---)の最大一致数は0
# string.z_algorithm('momomosumomosu')
# [14, 0, 4, 0, 2, 0, 0, 0, 4, 0, 2, 0, 0, 0]
# https://scrapbox.io/pocala-kyopro/Z_algorithm#5da262b94a4dd500007f6fb2
# https://qiita.com/Pro_ktmr/items/16904c9570aa0953bf05

# suffix array, prefix array, z algorithm
# ACL for Python 
# https://github.com/shakayami/ACL-for-python/blob/master/string.py
# https://github.com/shakayami/ACL-for-python/wiki/string

class string:
    def sa_is(s,upper):
        n=len(s)
        if n==0:
            return []
        if n==1:
            return [0]
        if n==2:
            if (s[0]<s[1]):
                return [0,1]
            else:
                return [1,0]
        sa=[0]*n
        ls=[0]*n
        for i in range(n-2,-1,-1):
            ls[i]=ls[i+1] if (s[i]==s[i+1]) else (s[i]<s[i+1])
        sum_l=[0]*(upper+1)
        sum_s=[0]*(upper+1)
        for i in range(n):
            if not(ls[i]):
                sum_s[s[i]]+=1
            else:
                sum_l[s[i]+1]+=1
        for i in range(upper+1):
            sum_s[i]+=sum_l[i]
            if i<upper:
                sum_l[i+1]+=sum_s[i]
        def induce(lms):
            for i in range(n):
                sa[i]=-1
            buf=sum_s[:]
            for d in lms:
                if d==n:
                    continue
                sa[buf[s[d]]]=d
                buf[s[d]]+=1
            buf=sum_l[:]
            sa[buf[s[n-1]]]=n-1
            buf[s[n-1]]+=1
            for i in range(n):
                v=sa[i]
                if v>=1 and not(ls[v-1]):
                    sa[buf[s[v-1]]]=v-1
                    buf[s[v-1]]+=1
            buf=sum_l[:]
            for i in range(n-1,-1,-1):
                v=sa[i]
                if v>=1 and ls[v-1]:
                    buf[s[v-1]+1]-=1
                    sa[buf[s[v-1]+1]]=v-1
        lms_map=[-1]*(n+1)
        m=0
        for i in range(1,n):
            if not(ls[i-1]) and ls[i]:
                lms_map[i]=m
                m+=1
        lms=[]
        for i in range(1,n):
            if not(ls[i-1]) and ls[i]:
                lms.append(i)
        induce(lms)
        if m:
            sorted_lms=[]
            for v in sa:
                if lms_map[v]!=-1:
                    sorted_lms.append(v)
            rec_s=[0]*m
            rec_upper=0
            rec_s[lms_map[sorted_lms[0]]]=0
            for i in range(1,m):
                l=sorted_lms[i-1]
                r=sorted_lms[i]
                end_l=lms[lms_map[l]+1] if (lms_map[l]+1<m) else n
                end_r=lms[lms_map[r]+1] if (lms_map[r]+1<m) else n
                same=True
                if end_l-l!=end_r-r:
                    same=False
                else:
                    while(l<end_l):
                        if s[l]!=s[r]:
                            break
                        l+=1
                        r+=1
                    if (l==n) or (s[l]!=s[r]):
                        same=False
                if not(same):
                    rec_upper+=1
                rec_s[lms_map[sorted_lms[i]]]=rec_upper
            rec_sa=string.sa_is(rec_s,rec_upper)
            for i in range(m):
                sorted_lms[i]=lms[rec_sa[i]]
            induce(sorted_lms)
        return sa
    def suffix_array_upper(s,upper):
        assert 0<=upper
        for d in s:
            assert 0<=d and d<=upper
        return string.sa_is(s,upper)
    def suffix_array(s):
        n=len(s)
        if type(s)==str:
            s2=[ord(i) for i in s]
            return string.sa_is(s2,255)
        else:
            idx=list(range(n))
            idx.sort(key=lambda x:s[x])
            s2=[0]*n
            now=0
            for i in range(n):
                if (i& s[idx[i-1]]!=s[idx[i]]):
                    now+=1
                s2[idx[i]]=now
            return string.sa_is(s2,now)
    def lcp_array(s,sa):
        n=len(s)
        assert n>=1
        rnk=[0]*n
        for i in range(n):
            rnk[sa[i]]=i
        lcp=[0]*(n-1)
        h=0
        for i in range(n):
            if h>0:
                h-=1
            if rnk[i]==0:
                continue
            j=sa[rnk[i]-1]
            while(j+h<n and i+h<n):
                if s[j+h]!=s[i+h]:
                    break
                h+=1
            lcp[rnk[i]-1]=h
        return lcp
    def z_algorithm(s):
        n=len(s)
        if n==0:
            return []
        z=[0]*n
        i=1;j=0
        while(i<n):
            z[i]=0 if (j+z[j]<=i) else min(j+z[j]-i,z[i-j])
            while((i+z[i]<n) and (s[z[i]]==s[i+z[i]])):
                z[i]+=1
            if (j+z[j]<i+z[i]):
                j=i
            i+=1
        z[0]=n
        return z

T = int(input())
for t in range(T):
    N = int(input())
    S = input()
    ZA = string.z_algorithm(S)
    #print('ZA', ZA)
    ans = 0
    for i in range(1, N):
        if ZA[i] == 0:
            if S[0] < S[i]:
                ans += 1
        elif ZA[i] >= i:
            if i < N-i:
                ans += 1
        elif 0 < ZA[i] < i:
            if i+ZA[i] < N and S[ZA[i]] < S[i+ZA[i]]:
                ans += 1
        #print('i', i, 'ZA[i]', ZA[i], 'ans', ans)
    print(ans)
0