簡単なプログラミング解説記事【AtCoder】【C問題】

その他
Screenshot
スポンサーリンク
スポンサーリンク

問題

問題文

英小文字からなる長さ N の文字列 S が与えられます。

S の空でない部分文字列であって、1 種類の文字のみからなるものの数を求めてください。 ただし、文字列として等しい部分文字列同士は、取り出し方が異なっても区別しません

なお、S の空でない部分文字列とは、S の先頭から 0 文字以上、末尾から 0 文字以上削除して得られる文字列のうち、長さが 1 以上であるもののことをいいます。 例えば、ab や abc は abc の空でない部分文字列ですが、ac や空文字列は abc の空でない部分文字列ではありません。

https://atcoder.jp/contests/abc329/tasks/abc329_c

例を通して問題内容を理解

入力例

6
aaabaa

出力例

4

説明

S の空でない部分文字列であって、1 種類の文字のみからなるものは aaaaaab の 4 つです。 S から a や aa を取り出す方法は 1 通りではありませんが、それぞれ 1 回ずつしか数えないことに注意してください。

解決策

アルファベット26個に全て対応すれば正解できそうだが、流石に面倒。

「なんかいい感じ」のがあるはず。

そこで、

ASCIIコードの
ord(‘a’) => 97
chr(97) => ‘a’

と、辞書型を使う。

実際にやってみた

max_counts = {chr(i): 0 for i in range(ord(‘a’), ord(‘z’)+1)}

これで、aからzまでの「辞書」が完成。

その後、辞書を更新する。

例えばakaaという文字列なら、

rle_S = [‘aka’, [1, 1, 2]]

なので、1文字目のaで

{ ‘a’ : 1, ~~~~}

になるが、3,4文字目のaaで

{ ‘a’ : 2, ~~~~}

に更新される。

あとは、辞書に載っている数字(=文字列Sにおける、アルファベットごとの最大の連続回数)の合計すれば良い。

ソースコード

def rle(s):

n = len(s)

if n == 0:
return '', []

ret = ['', []]
c = s[0] # 1文字目がどこまで続くか
cnt = 0

for i in range(n):
if c == s[i]:
cnt += 1
else:
ret[0] += c # 1文字目の連続具合の記録
ret[1].append(cnt)
c = s[i] # 次の文字に移る
cnt = 1

if cnt > 0: # 例えばAAABBだったら、B,2を記録する場所
ret[0] += c
ret[1].append(cnt)

return ret

N = int(input())
S = input()
rle_S = rle(S)

# 各文字の最大連続回数を記録する辞書を初期化
max_counts = {chr(i): 0 for i in range(ord('a'), ord('z')+1)}

for i in range(len(rle_S[0])):
char = rle_S[0][i]
count = rle_S[1][i]
if max_counts[char] < count:
max_counts[char] = count

# 今後、'a'から'z'までの任意の文字の最大連続回数は max_counts['a'], max_counts['b'], ..., max_counts['z'] でアクセスできます

print(sum(max_counts.values()))

コメント

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