summer_tree_home

Check iOでPython3をマスターするぜっ

The Most Wanted Letter (Check iO > Home)

どんな問題?

与えられた文字列から、最頻出の文字を返す。例えば、"abbccc"なら、aが1回、bが2回、cが3回出てくるから、答えはc となる。

  • 大文字小文字は区別しない。答えは小文字とする。
  • 記号、スペースは無視すること。
  • 答えが複数ある時は、アルファベット順で前の文字が優先となる。 'aabbcc' なら 'a'

例題はこんな感じ。
checkio("Hello World!") == "l"
checkio("How do you do?") == "o"
checkio("One") == "e" # カウントが同じならアルファベット順
checkio("Oops!") == "o" # 大文字小文字は区別しない
checkio("AAaooo!!!!") == "a"

どうやって解く?

まず最初に、lower()で小文字にする。そこからどうすればいいのか、かなり悩んでしまった。
同じ出現数が複数あるときは、アルファベット順で比較するわけだから、最初からアルファベット順でリスト化しておけばわかりやすいか。a-zの26文字の出現数を調べて、ソートすればいいかな?と考えて作ったのがこちら。

def checkio(text):
    text = text.lower()
    lst = [(chr(i), text.count(chr(i))) for i in range(ord('a'), ord('z') + 1)]
    lst.sort(key=lambda x: (-x[1], x[0]))
    return lst[0][0]

'a'~'z'を作成する方法がわからずに、range(ord('a'), ord('z') + 1) なんて書いてしまったが、 string.ascii_lowercase を使えばよかった。書き直しておく。

import string

def checkio(text):
    text = text.lower()
    lst = [(c, text.count(c)) for c in string.ascii_lowercase]
    lst.sort(key=lambda x: (-x[1], x[0]))  # OrderByDesc(count).ThenBy(char)
    return lst[0][0]

簡単に処理を説明すると、

  1. 文字列を小文字にする。
  2. 'a'~'z'のそれぞれについて出現数を計算して、(char, count)タプルのリストを作成する。
  3. リストを、まずcountで降順にソートし、次にcharで昇順にソートする。(コメントには、C#風の説明を付けておいた。)
  4. リストの先頭に答えがある!

なかなか綺麗に書けたと思うのだがどうだろうか。じゃあ、他の人の解答を見てみよう。

他の人の答え

def checkio(text):
    return max(string.ascii_lowercase, key=lambda ch: text.lower().count(ch))

え?1行だけ?ちょっと何をしてるのかわからないぞ・・・。えーと、 string.ascii_lowercase つまり 'abc...xyz' の最大値を求める。値を求めるkeyは、文字列中の出現数、というわけか。
最頻出の文字が複数ある時はアルファベット順、というのはどこで処理してるんだ?

max(iterable, *[, key])
最大の要素が複数あるとき、この関数はそのうち最初に現れたものを返します。
http://docs.python.jp/3.3/library/functions.html?highlight=max#max

なるほど、maxがうまいこと処理してくれるんだな。知らなかった。

もう一つ気になるのは、keyが繰り返し計算されるんじゃないのかという点。 text.lower().count('a') が何回も繰り返されるのであれば、無駄が多いように思うが、どうなんだろ?それとも最初にすべてのkeyを計算してから比較するのかな?
・・・これについては、実際に調べてみた結果、 text.lower().count('a') は1回しか呼ばれてなかった。そうなのか、すごいなPython。えっ当たり前?

もう一つ、よく似た解答を見つけた。

def checkio(text):
    text = text.lower()
    return max(string.ascii_lowercase, key=text.count)

こっちはさらにシンプルだ。key=labmda ch: text.count(ch) を key=text.count と書いたわけか。なーるほど。


今回も、自分では思いつけない解答があって、勉強になった。
なんか自分の解答を得意げにPublishedしたのが恥ずかしくなってきたよ。