Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonで解くミニオンゲーム:勝者の名前とスコアを求める方法

この記事では、「ミニオンゲーム」と呼ばれる文字列ゲームについて、勝者の名前とスコアを求めるPythonプログラムをわかりやすく解説します。

ゲームのルール

アマル(Amal)とビマル(Bimal)という2人のプレイヤーが、同じ文字列 s を使って対戦します。ルールは以下の通りです。

  • 両プレイヤーとも同じ文字列 s を持ちます。
  • それぞれ、s の文字を使って部分文字列(サブストリング)を作ります。
  • ビマルは子音で始まる部分文字列だけを作ります。
  • アマルは母音で始まる部分文字列だけを作ります。
  • 両者が作成可能なすべての部分文字列を作り終えた時点でゲーム終了です。

スコアの計算方法

採点基準はシンプルです。作った部分文字列が元の文字列 s の中に出現するたびに1点が入ります。すべての部分文字列の出現回数を合計し、より高いスコアを獲得したプレイヤーが勝者となります。

例:「BANANA」の場合

入力が s = "BANANA" のとき、出力は「Bimal, 12」になります。内訳を表に整理すると次のようになります。

文字列:BANANA
アマル(Amal)ビマル(Bimal・勝者)
部分文字列スコア部分文字列スコア
A3B1
AN2N2
ANA2BA1
ANAN1NA2
ANANA1BAN1
  NAN1
  BANA1
  NANA1
  BANAN1
  BANANA1
合計 9合計 12

母音始まりの部分文字列の合計が9点、子音始まりの合計が12点となり、ビマルの勝利です。

効率的な解法のポイント

すべての部分文字列を実際に生成して数えると、時間計算量はO(n²)以上になり非効率です。ここで重要な気づきがあります。

インデックス i の文字から始まる部分文字列は、ちょうど「文字列の長さ − i」個存在します。

たとえば長さ6の "BANANA" では、先頭の 'B'(i=0)から始まる部分文字列は "B"、"BA"、"BAN"、"BANA"、"BANAN"、"BANANA" の6個です。つまり、各文字が母音か子音かを判定し、「n − i」を対応するプレイヤーのスコアに加算していくだけで、文字列を一度走査するだけで(O(n)で)合計スコアを求められます。

アルゴリズムの手順

  • 母音の集合 vowels を用意します。
  • スコア変数 p1(子音側=ビマル)、p2(母音側=アマル)を0で初期化します。
  • 文字列中の各インデックス i と文字 c について次を繰り返します。
    • c が母音なら、p2 に「文字列の長さ − i」を加算する
    • それ以外なら、p1 に「文字列の長さ − i」を加算する
  • p1 > p2 なら「Bimal」と p1 を返します。
  • p2 > p1 なら「Amal」と p2 を返します。
  • 同点の場合は「Draw」を返します。

Python実装例

以下のコードで、実際の動作を確認してみましょう。

def solve(word):
    vowels = set('AEIOU')
    p1 = 0
    p2 = 0
    for i, c in enumerate(word):
        if c in vowels:
            p2 += len(word) - i
        else:
            p1 += len(word) - i
    if p1 > p2:
        return 'Bimal', p1
    elif p2 > p1:
        return 'Amal', p2
    else:
        return 'Draw'

word = "BANANA"
print(solve(word))

入力

"BANANA"

出力

('Bimal', 12)

まとめ

この問題は一見、すべての部分文字列を列挙する必要がありそうですが、「位置 i の文字から始まる部分文字列は n − i 個ある」という性質を利用することで、線形時間 O(n) で勝者とスコアを求められます。文字列処理の定番テクニックのひとつなので、競技プログラミングやコーディング面接でも役立つ考え方です。ぜひ自分でも別の文字列で試してみてください。

  1. 【Python】配列ゲームの勝者を見つけるプログラムの実装方法

    問題の概要 一意な要素のみを含む配列「arr」と、整数値「k」が与えられているとします。ここで、次のようなゲームを考えてみましょう。 各ターンでは、配列の先頭2つの要素 arr[0] と arr[1] を比較します。大きい方の値が勝者となって位置0に残り、負けた小さい方の値は配列の末尾へ移動します。このゲームは、いずれかの値が k回連続 で勝利した時点で終了し、その時点での勝者が答えとなります。私たちの課題は、この配列から勝者を見つけることです。 具体例 例えば、入力が arr = [1,5,6,3,4,2]、k = 3 の場合、出力は 6 になります。その過程は以下の通りです。 第1ラウ

  2. Pythonでi個のx、j個のy、k個のzからなる部分列の個数を求めるプログラム

    文字列 s が「x」「y」「z」で構成されているとします。このとき、「x」が i 個(i ≥ 1)、その後に「y」が j 個(j ≥ 1)、さらにその後に「z」が k 個(k ≥ 1)という順序で並ぶ部分列の総数を求めます。 例えば、入力が s = xxyz の場合、出力は 3 になります。これは xyz を2通りと xxyz を1通りの合計3通り作れるためです。 解き方のアプローチ この問題は動的計画法(DP)の考え方を使うと効率的に解けます。文字列を先頭から順に走査しながら、次の3つのカウンターを更新していきます。 x: それまでに見つかった「xのみで構成される部分列」の個数 y: そ