Python
世界で闘うプログラミング力を鍛える150問

"世界で闘うプログラミング力を鍛える本"をPythonで解いてみよう!

Pythonで競プロみたいなことしたい!

ということで一部界隈では有名な世界で闘うプログラミング力を鍛える本をPythonで解いてみました。
問題の解説とか詳しく知りたい方はぜひ本書を買ってみてください(ステマじゃないよ
ちなみに今回の解き方はテストを実装しているので、無駄に長くなっています。なので前半だけ見れば十分です。

ではやっていこー1章!

ある文字列が重複のないものかどうか調べよ

1-1.py
import unittest

# ある文字列をSとする
# 言語は日本語、文字コードはUnicode (日本語3byte)を想定

# 解法1: O(N^2)


def unique(S):
    if len(S) > 2**24:
        return False
    for index in range(len(S)):
        if S[index] in S[index + 1:]:
            return False
            break
        elif index == len(S) - 1:
            return True

# 解法2: 時間はO(N)だが、2^24の長さのリストをつくるのでメモリを喰うゾイ
def unique2(S):
    if len(S) > 2**24:
        return False
    char_set=[False for _ in range(2**24)]
    for char in S:
        uni_num=ord(char)
        if char_set[uni_num]:
            return False
        char_set[uni_num] = True
    return True

class Test(unittest.TestCase):
    dataT = [("こんにちは"), ("わたしは"), ("ぺっぱーです")]
    dataF = [("ようよう"), ("おれさまのなまえは"), ("じゃいあんだぜい")]

    def test_unique(self):

        for test_string in self.dataT:
            torf = unique(test_string)
            self.assertTrue(torf)

        for test_string in self.dataF:
            torf = unique(test_string)
            self.assertFalse(torf)


if __name__ == "__main__":
    unittest.main()

2つの文字列があるとき、片方の文字の並び替えでもう片方の文字列は作れるか調べよ!
(未完です。)

1-2.py
# 2つの文字列をS1, S2とする。
# ソートして比較

# 解法1: 文字列をリストにしてソート。比較。


def p_string1(S1, S2):
    S1_l = list(S1)
    S2_l = list(S2)
    if len(S1) != len(S2):
        return False
    elif S1_l.sort() == S2_l.sort(): # TODO: はいここが駄目。アルファベットと数字が混ざるとソートできないからエラーになる。
        return True

# p_string("dog ", "g od") > OK!

# 解法2: 出現回数をカウント
# ここではcounterモジュールを使わずに辞書を用いて実装する


def p_string2(S1, S2):
    if len(S1) != len(S2):
        return False
    counter = {}  # 辞書をつくる
    for string in S1:
        if string in counter:  # 辞書は持っていないキーを指定して演算するとエラーを起こすので、キーを持っているか確認が必要
            counter[string] += 1
        else:
            counter[string] = 1
    for string in S2:
        if string not in counter:  # 辞書は持っていないキーを指定して演算するとエラーを起こすので、キーを持っているか確認が必要
            return False
        if counter[string] == 0:
            return False
            counter[string] -= 1
        return True

# TODO: Test書きたい。
# text1="dogrun"
# text2="godnur"
# print(p_string1(text1,text2))
# print(p_string2(text1,text2))

# 2018/3/16追記
# 上の書き方はいけてない。
import unittest
class test(unittest.TestCase):
    dataT = (
        ('abcd', 'bacd'),
        ('3563476', '7334566'),
        ('wef34f', 'wffe34'),
    )
    dataF = (
        ('abcd', 'd2cba'),
        ('2354', '1234'),
        ('dcw4f', 'dcw5f'),
    )
    def test_cp(self):
        # true check
        for test_strings in self.dataT:
            result = p_string2(*test_strings)
            self.assertTrue(result)
        #false check
        for test_strings in self.dataF:
            result = p_string2(*test_strings)
            self.assertFalse(result)

if __name__ == "__main__":
    unittest.main()

空白スペースを"%20"で置換せよ

1-3.py
import unittest


def URLify(S , length):
    return S.replace(" ", "%20")


# TODO:Test書かなあかん
# text = "google  ha sugoi kamo"
# L = len(text)
# print(URLify(text,L))

# 3/21 テスト書いた
class Test(unittest.TestCase):
    data = [
        ('overwatch love  is strong', 25,
         'overwatch%20love%20%20is%20strong'),
        ('kakuno mendo', 12,
        'kakuno%20mendo')
    ]

    def test_URLify(self):
        for [test_string, length, answer] in self.data:
            actual = URLify(test_string, length)
            self.assertEqual(actual, answer)


if __name__ == "__main__":
    unittest.main()

一つの文字列が与えられたとき、文字を並び替えて回文が作れるか調べよ!

1-4.py
# 回文にできるかは文字数が偶数のときそれぞれの文字出現回数cが2|c(偶数回)であることが必要十分
# 文字数が奇数のときはc = oddとなるようなcが一つ。その他は2|cであることが必要十分。
# たぶん回答はASCII想定なので、ここでは言語は英語、コードはASCIIとする。アルファベットずるすぎンゴ

import unittest


def palindrome(string):
    string = string.lower()
    counter = [0 for _ in range(128)]
    if len(string) % 2 == 0:
        for char in string:
            pos = ord(char)
            counter[pos] += 1
        for count in counter:
            if count % 2 == 1:
                return False
        return True
    else:
        for char in string:
            pos = ord(char)
            counter[pos] += 1
        odd_counter = 0
        for char in counter:
            if char == 1:
                odd_counter += 1
        return(odd_counter == 1)


text = "wasi hah siwa"
print(palindrome(text))


class Test(unittest.TestCase):
    data = [
        ("I love evole ei ", True),
        ("Tomato", False),
        ("Tomamto", True),]
        ("wasi hah siwa", True)]
    # data = [
    #     ('Tact Coa', True),
    #     ('jhsabckuj ahjsbckj', True),
    #     ('Able was I ere I saw Elba', True),
    #     ('So patient a nurse to nurse a patient so', False),
    #     ('Random Words', False),
    #     ('Not a Palindrome', False),
    #     ('no x in nixon', True),
    #     ('azAZ', True)]

    def test_palindrome(self):
        for [test_string, torf] in self.data:
            result = palindrome(test_string)
            self.assertEqual(result, torf)


if __name__ == "__main__":
    unittest.main()

2つの文字列が与えられたとき、文字の挿入、削除、置き換えのどれか1回の操作でもう片方の文字列はつくれるか調べよ!

1-5.py
# 文字数変動が2以上だったらFlase
# 文字数変動が1のとき、文字種変動はS1(文字数おおいほう).pop(i for i in S1)==S2となる。
# 文字数変動がないとき、stringをリストとしみなすとS1,S2では一つの要素以外同じ。

import unittest


def arr_char(S1, S2):
    if len(S1) == len(S2):
        diff_count = 0
        for i in range(len(S1)):
            if S1[i] not in S2[i]:
                diff_count += 1
        if diff_count == 1:
            return True
        return False
    if abs(len(S1) - len(S2)) == 1:
        if len(S2) > len(S1):
            tmp = S2
            S2 = S1
            S1 = tmp
        S1_l = list(S1)
        for i in range(len(S1)):
            S1_l.pop(i)
            re_S1 = "".join(S1_l)
            if re_S1 == S2:
                return True
            S1_l = list(S1)
        return False
    return False


class Test(unittest.TestCase):
    dataT = [
        ("pale", "ple", True),
        ("tre", "true", True),
        ("truu", "true", True),  # u2,e0→u1,e1
        ("truu", "twue", False)  # u2, r1, w0, e0, u1, r0, w1, e1 文字の出現回数でカウントは意味ないか
    ]

    def test_arr_char(self):
        for [S1, S2, torf] in self.dataT:
            actual = arr_char(S1, S2)
            self.assertEqual(actual, torf)


if __name__ == "__main__":
    unittest.main()

ある文字列の重複を数字にしろ!

1-6.py
# ポインタつくって動かしていけばええんちゃうか?
# 英語だったらASCII想定で、リストのインデックスと対応させる。日本語だったらUTF8想定で辞書つくったほうがいい。
# 3/24追記。それだめや。aabbaaだとa2b2a2がa4b2となってまう。
import unittest

#
# def zip_string(string):
#     sl = list(string)
#     count_list=[0 for _ in range(128)]
#     counter = 1
#     for i in range(len(string)):
#         a = sl.pop(i)
#         count_list[ord(a)] +=1
#         if a == sl[i]:
#             count_list[ord(a)] += 1
#         sl=list(string)


def zip_string(string):
    result = ""
    str_result = ""
    count = 1
    for i in range(0, len(string) - 1):
        char = string[i]
        if string[i] == string[i + 1]:
            count += 1
            if i == len(string) - 2:
                result += char + str(count)
        else:
            result += char + str(count)
            count = 1
            if i == len(string) - 2:
                result += string[-1] + str(1)

    str_result = result[0::2]
    if str_result == string:
        return string
    else:
        return result


class Test(unittest.TestCase):
    data = [
        ("aaaabbcccd", "a4b2c3d1"),
        ("abcd", "abcd"),
        ("aaabbbFFFkkk", "a3b3F3k3")
    ]

    def test_zip_string(self):
        for [string, answer] in self.data:
            self.assertEqual(zip_string(string), answer)


if __name__ == "__main__":
    unittest.main()

行列を90度回転させろ!

1-7.py
import unittest

# 問題の意味がわからないが、とりあえず行列を90度回転させるメソッドを書く。
# テストから書き始めるの結構大事かも。
# numpyでやれよって思ったけど、numpyのメソッドなくてビビった。ほえほえ.

# 一回連結させて区切る?


def rotataion(matrix):
    rows = len(matrix)
    columns = len(matrix[0])
    result = []
    for i in range(columns):
        result.append([])
    for n in range(rows):
        for m in range(columns):
            result[columns - m - 1].append(matrix[n][m])
    return result


class Test(unittest.TestCase):
    data = [
        ([
            [1, 2, 3, 4],
            [5, 6, 7, 8],
            [9, 10, 11, 12]
        ], [
            [4, 8, 12],
            [3, 7, 11],
            [2, 6, 10],
            [1, 5, 9]
        ])
    ]

    def test_rotation(self):
        for matrix, answer in self.data:
            actual = rotataion(matrix)
            self.assertEqual(actual, answer)


if __name__ == "__main__":
    unittest.main()

行列のある要素が0だったら、その行と列を全部0にしろ!連帯責任だ!

1-8.py
import unittest

# def zero_mat(matrix):
#     for n in matrix:
#         for m in len(matrix[0]):
#             if 0 in n:
#                 n =[0 for i in n]
#             if
#列を扱えないのは辛い。やりなおし。

# 答え引用しました。や、やり方はわかってたんだからね・・・!?

def zero_matrix(matrix):
    m = len(matrix)
    n = len(matrix[0])
    rows = []
    cols = []

    for x in range(m):
        for y in range(n):
            if matrix[x][y] == 0:
                rows.append(x)
                cols.append(y)

    for row in rows:
        nullify_row(matrix, row)

    for col in cols:
        nullify_col(matrix, col)

    return matrix

def nullify_row(matrix, row):
    for i in range(len(matrix[0])):
        matrix[row][i] = 0


def nullify_col(matrix, col):
    for i in range(len(matrix)):
        matrix[i][col] = 0


class Test(unittest.TestCase):
    '''Test Cases'''
    data = [
        ([
            [1, 2, 3, 4, 0],
            [6, 0, 8, 9, 10],
            [11, 12, 13, 14, 15],
            [16, 0, 18, 19, 20],
            [21, 22, 23, 24, 25]
        ], [
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [11, 0, 13, 14, 0],
            [0, 0, 0, 0, 0],
            [21, 0, 23, 24, 0]
        ])
    ]

    def test_zero_matrix(self):
        for [test_matrix, expected] in self.data:
            actual = zero_matrix(test_matrix)
            self.assertEqual(actual, expected)

if __name__ == "__main__":
    unittest.main()

2つの文字列があるとき、片方を切ってつなげたらもう片方の文字列になるか!?

1-9.py
# これは日本語では問題が意味不。

import unittest

def isSubstring(S1,S2):
    if S1 in S2:
        return True
    return False

def once_isSubstring(S1,S2):
    return isSubstring(S2, S1+S1)

class Test(unittest.TestCase):
    data=[
    ("abcdefg", "defgabc",True),
    ("coffeebottle", "bottledcoffe", False)
    ]
    def test_once_isSubstring(self):
        for [s1,s2,torf] in self.data:
            actual=once_isSubstring(s1,s2)
            self.assertEqual(actual,torf)
if __name__=="__main__":
    unittest.main()

Done!

肝は実行時間がどうとかいうことだけど、Pythonでも書けるんだぞってことを示したかった。
ただ2章以降の連結リストとかは食指が進まないので、誰か解いてくださいお願いします。