【paiza】文字列収集 (paizaランク S 相当) 問題をPythonで実行・解説

# 本投稿の趣旨

以下の問題をpythonでコーディングの解説を行います。


https://paiza.jp/works/mondai/real_event/word_collection

:::note 
問題
あなたは文字列の愛好家で、文字列を収集することにとても熱心です。

文字列は市場で高値で取引されています。今、市場には N 個の文字列が出まわっており、文字列 S_i (1 ≦ i ≦ N) の価格は P_i です。 あなたは数ある文字列の中でも、とくに先頭がある特定の文字列で始まる文字列に興味があり、そのような文字列をすべて買い占めたいです。例え、同じ文字列が複数売っていたとしてもそのすべてを買います。

そこで、市場に出回っている N 個の文字列 S_i とそれぞれの価格 P_i (i ≦ i ≦ N)、また、M 個のクエリ文字列 Q_i (1 ≦ i ≦ M) が与えられるので、それぞれのクエリ Q_i に対し、市場に出回っている文字列の中で先頭が Q_i で始まる文字列すべてを買い占めるのに必要な金額を出力するプログラムを作成してください。
:::
各変数や出力方法など、各種条件は上記サイトをご覧ください。


問題が直感的にわかりにくいので、以下具体例…
例えば、入力が以下6行の場合、

```ruby:
3 2 #先頭3行は市場にある文字列・後尾2行は欲しいクエリ文字列
a 1 #aという文字列の金額が1
b 2
ab 3 
a #aから始まる文字列の合計金額を計算
b
```

出力は、
4 (a=1,ab=3の2つを足して4)
2 (b=2の1つで2)

となるわけです。


# プログラムの流れ

1. データ入力
1. データ分割
1. 計算する関数の定義
1. 計算結果を出力


# コード全体

```ruby:

# 1.データの入力
data = """
5 3
paiza 16
pizza 1
paizaio 4
paizapoh 2
pizzaio 8
paiza
pizza
p
"""


# 2.入力データを「先頭行」「文字列」「クエリ」に3分割

lines = data.strip().split('\n')

 # 先頭行
N, M = map(int, lines[0].split())

 # 文字列
strings = []
for i in range(1, N + 1):
    s, p = lines[i].split()
    strings.append((s, int(p)))

 # クエリ
queries = lines[N + 1:]


# 3.関数の定義
def calculate_total_price(N, M, strings, queries):

    # 結果を格納するリスト
    results = []
    
    # クエリごとに計算
    for query in queries:
        total_price = 0
        
        # 市場に出回っている文字列をすべてチェック
        for s, p in strings:
            if s.startswith(query):
                total_price += p
        
        # 結果をリストに追加
        results.append(total_price)
    
    return results


# 4.計算結果の出力
results = calculate_total_price(N, M, strings, queries)
for result in results:
    print(result)
# 各行ごとに出力
```
→→→出力結果

22
9
31



# コードの解説

###  1.データの入力（下記は一例）
```ruby:
data = """
5 3
paiza 16
pizza 1
paizaio 4
paizapoh 2
pizzaio 8
paiza
pizza
p
"""
```
この入力データは1行目に、
「N個の文字列とM個のクエリがありますよ」と教えてくれるので、優しいですね。
この1行目をなくした課題にしてもいいような気がしました。


### 2.入力データを「先頭行」「文字列」「クエリ」に3分割
```ruby:
# dataをリスト化
lines = data.strip().split('\n')

 # 先頭行
N, M = map(int, lines[0].split())

 # 文字列
strings = []
for i in range(1, N + 1):
    s, p = lines[i].split()
    strings.append((s, int(p)))

 # クエリ
queries = lines[N + 1:]
```

strip()：文字列の先頭と末尾にある空白文字（スペース、タブ、改行など）を削除
split('\n') ：文字列を改行文字（\n）で分割し、各行をリストの要素として取得


上記2つを通してdataは、
['5 3','paiza 16','pizza 1','paizaio 4','paizapoh 2','pizzaio 8','paiza','pizza','p']

となります。

このdataを変換したリストlinesから、「先頭行（1つ目の要素）」「文字列（N個目までの要素）」「クエリ（N+1個目以降の要素）」の3つに分解します。


### 3.関数の定義
```ruby:
def calculate_total_price(N, M, strings, queries): 
# 変数(文字列の数,カラムの数,文字列と金額のリスト,買うカラムのリスト)

    # 結果を格納するリスト
    results = []
    
    # クエリごとに金額を計算
    for query in queries:
        total_price = 0
        
        # 文字列の頭文字をチェック
        for s, p in strings:
            if s.startswith(query):
                total_price += p
        
        # 結果をリストに追加
        results.append(total_price)
    
    return results
```
startswith(query) は、文字列 s がクエリ文字列 query で始まるかどうかを確認するメソッドです。もし s が query で始まるならば、条件が True となり、次のステップへ進み、金額pを追加します。


### 4.計算結果の出力
```ruby:
# 4.計算結果の出力
results = calculate_total_price(N, M, strings, queries)
for result in results:
    print(result)
```

→→→出力結果

22
9
31


これにて、終了です。