典型90【006 – Smallest Subsequence(★5)】をpython解説

Python

AtCoder の問題を解くために必要な実力を付けるために作られた「典型問題」を解いていく企画として、「典型90」があります。

今回は、その内、の6問目である006 – Smallest Subsequence(★5)を解説していきたいと思います。

問題名 【006 – Smallest Subsequence(★5)】

006 – Smallest Subsequence(★5)

考察

基本的には、aを取っていきたい。が、

N=3,K=2, S = zza の場合
最初の文字にaを取ってしまうと、それ以上の文字を取ることができない。

Q 何文字目まで取ることができるのか?

取った文字のカウントをcntとすると、残り取らなきゃいけない文字列の量は、K-cntです。

N = 7, K = 3, S = atcoderの場合

cnt=0のとき

atcod (7-3=4文字目まで取ることができる)
→aを取る

cnt=1のとき

tcode (7-3+1=5文字目まで取ることができる)
→cを取る

cnt=2のとき

oder (7-3+2=6文字目まで取ることができる)
→dを取る

この例からも、N-K+cnt文字列まで取ることが可能です。

手順

なので、実装内容は、

  • N*26の2次元配列を作成する
  • 0~Nで、i文字目以降に現れるa,b・・・zの場所を記憶しておく
  • 0~N文字目までについて、for文を回す
  • a~Zについて見ていき、もしaがN-K+cnt文字列目にあったら採択し、なかったらbについて見る
  • cnt == Kになるまで行う

典型解法:辞書順最小を貪欲法

コード

import sys
def LI(): return list(map(int,sys.stdin.readline().rstrip().split()))
def II(): return int(sys.stdin.readline())
def MI(): return map(int,sys.stdin.readline().rstrip().split())
def S(): return sys.stdin.readline().rstrip()
readline = sys.stdin.readline
sys.setrecursionlimit(10 ** 6)

N,K = MI()
letter = S()

# ord('a')→97
# chr(97)→a
INF = 10**18
dp = [[INF for i in range(26)] for i in range(N+1)]
#N+1行目は、使わないけど、dp[i][j]=dp[i+1][j]とするときに必要になる

for i in range(N-1,-1,-1):#後ろからみる
    for j in range(26):#a~zの26もじについて
        dp[i][j] = dp[i+1][j]#i文字目以降でj番目のアルファベットがはじめて出てくるのは、
    dp[i][ord(letter[i]) - ord("a")] = i#i文字目がj番目のアルファベットのときのみ、= i

ans = ""
cnt = 0
now = 0
while cnt !=K:#文字列の長さはKになるまで
    for j in range(26):#辞書順最小のaから順に
        if N - dp[now][j] >= K-cnt:
            #now番目の文字にアルファベットj番目の文字を使用したとき
            # もし、N-dp[now][j](残り使える文字数) >= K-cnt(目標の文字列までの数)ならok
            ans += chr(ord('a') + j)
            #次の探索範囲は、現在使用した文字のindex+1からなので
            now = dp[now][j] + 1
            #文字列カウント+1
            cnt += 1
            break
print(ans)

まとめ

a~zの26の情報を用いて、N*26の二次元配列にするのか・・・
初見じゃ全く検討つかなかった・・・

前の問題【004 – Cross Sum(★2)】

次の問題【007 – CP Classes(★3)】

コメント

タイトルとURLをコピーしました