Pythonで文字列から出現頻度の最も高い文字を調べる方法

Python most char Python

今日もChickIOで1問問題を解いたので、解きながら考えていたことをブログにメモしておきます。
解き方は思いついているのにFor文のループ回数間違えたせいで時間がかかりました。

室井
妖怪1足りないの仕業に違いありません!
みゅう
久々に聞きましたねそのワード。

問題(The Most Wanted Letter)

英字と記号を含む文字列から最も出現頻度の高い文字を見つけ、小文字で返す関数を作成する。

文字を数える際に大文字と小文字は区別せず、 記号、数字、空白は無視する。
2文字以上が同じ頻度の場合はアルファベットで最初にくる文字を返す。

入力される文字列はASCIIで長さが1~10⁵とする。

例)“Hello World!”なら”l”を返す。

僕のコード

import re

def checkio(text: str) -> str:
    str_list = re.findall("[a-z]",text.lower())
    str_length = len(str_list)
    max = 0
    check_list = []
    most_char = str_list[0]
    
    for i in range(str_length):
        cnt = 0
        if str_list[i] not in check_list:
            check_list.append(str_list[i])
            for j in range(str_length):
                if str_list[i] == str_list[j]:
                    cnt += 1
            if cnt > max or cnt == max and str_list[i] < most_char:
                most_char = str_list[i]
                max = cnt

    #replace this for solution
    return most_char

if __name__ == '__main__':
    print("Example:")
    print(checkio("Hello World!"))

    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert checkio("Hello World!") == "l", "Hello test"
    assert checkio("How do you do?") == "o", "O is most wanted"
    assert checkio("One") == "e", "All letter only once."
    assert checkio("Oops!") == "o", "Don't forget about lower case."
    assert checkio("AAaooo!!!!") == "a", "Only letters."
    assert checkio("abe") == "a", "The First."
    print("Start the long test")
    assert checkio("a" * 9000 + "b" * 1000) == "a", "Long."
    print("The local tests are done.")

考え方

大文字と小文字をまとめて処理する

大文字と小文字を区別せずにカウントするので、同じコードでカウントできるようにどちらかを変換して合わせることにしました。

すべての文字を小文字にするlowerメソッドか、大文字にするupperメソッドを使えば良さそうですね。
今回は戻り値を小文字で返す問題なので、lowerメソッドを使っています(4行目)。

記号、数字、空白を取り除く

記号、数字、空白は邪魔なので、取り除いてしまいます。

findallメソッドで正規表現にマッチする文字列全てをリストにできますから、
re.findall(“[a-z]”,text.lower())
と書けば記号、数字、空白を除外したリストにできますね(4行目)。

findallメソッドより先にlowerメソッドが読み込まれるので、大文字は小文字に変換されてリストに含まれます。

import re を忘れずに書いておきましょう。

2重ループで出現頻度の高い文字を見つける

リストの文字を比較して、同じ文字だった際にカウントしています。
ループの繰り返しはどちらもrange関数を使って、0~文字数-1としました。

check_listという空のリストを用意して、一度数えた文字はappendメソッドで追加しています。
check_listに既に同じ文字が入っている場合、内側のループを実行しないようにします。

室井
文字列が(“a” * 9000 + “b” * 1000)とかだったら、”a”の個数を9,000回も数え直すのは無駄だからね。

2文字以上が同じ頻度の場合は、文字列を比較して小さい場合に代入するようにしました。

あとがき

ChickIOは問題を解いたあとに他の人のコードを見ることができるんですが、たった3行で同じ処理を書いている人もいました。
僕の知らないモジュールを使っているので今はまだ理解できませんが、そういうコードを書けるようになりたいですね。