summer_tree_home

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

Three words (Library 2.0) - 3つ連続した単語を探せ

どんな問題?

Three words
http://www.checkio.org/mission/three-words/

スペースで区切られた複数の単語や数字が含まれている文字列がある。
単語は文字(a-zA-Z)のみを含むものとする。
文字列に、3つ連続した単語が含まれているかをチェックせよ。

なんか問題文の意味が把握できなくて苦労したが、よーするに、単語が3つ続いてればOKということだろう。
「数字 単語 単語 単語 数字」は、True
単語 単語 数字 単語 単語」は、False

例題:

checkio("Hello World hello") == True
checkio("He is 123 man") == False
checkio("1 2 3 4") == False
checkio("bla bla bla bla") == True
checkio("Hi") == False

どうやって解く?

単語か数字かの判定は、ヒントにあるようにisalpha()で判断すればいいとして。

連続して3つ、という部分をどうするか。
まず、引数の文字列、例えば "He is 123 man" を [True, True, False, True] というboolのリストに変換して、そこに連続3つのTrueが出てくるかどうかを調べることにする。

文字列なら部分文字列が含まれているかどうかは、 in を使えばいい。リストをいったん文字列に変換して調べることにする。

def checkio(words):
    tokens = words.split(' ')
    s = ''.join(['1' if t.isalpha() else '0' for t in tokens]
    return '111' in s

文字列 "He is 123 man" を、boolのリスト [True, True, False, True] に変換する。
TrueとFalseを、'1'と'0'に置き換えて文字列 '1101' にする。
この中に '111' が含まれているかをチェック。

1行で書くとこうなる。

def checkio(words):
    return '111' in ''.join(['1' if t.isalpha() else '0' for t in words.split(' ')])

テストはクリア&ひとまず公開。

別解を考える

いったん文字列に変換する、というのはやはり正攻法ではない気がする。
もっとストレートに、リストの中から部分リストを検索するにはどうしたらいいかな?

シーケンス(seq)に、部分シーケンス(sub_seq)が含まれているかどうかを調べる関数を作ってみた。

def contains(seq, sub_seq):
    # Return True if the sequence contains the sub-sequence
    return any(sub_seq == seq[i:i + len(sub_seq)] for i in range(len(seq) - len(sub_seq) + 1))

contains([1, 2, 3, 4, 5], [3, 4])  # True
contains([1, 2, 3, 4, 5], [4, 3])  # False

リストに限らず、タプルや文字列、ジェネレータでも使える。(seqとsub_seqは同じタイプでなくてはならない。)

全体はこうなった。

def contains(seq, sub_seq):
    return any(sub_seq == seq[i:i + len(sub_seq)] for i in range(len(seq) - len(sub_seq) + 1))

def checkio(words):
    seq = [t.isalpha() for t in words.split(' ')]
    sub_seq = [True] * 3
    return contains(seq, sub_seq)

せっかくなので、こっちも公開しておこう。