Pythonで解く文字並べ替えゲーム:回文を作れるプレイヤーの勝者を判定するアルゴリズム
問題の概要
小文字の英字のみで構成される文字列 S があるとします。この文字列を使って、2人のプレイヤーが次のルールでゲームを行います。
- どの手番においても、文字列の文字を自由に並べ替えて回文(前から読んでも後ろから読んでも同じになる文字列)を作ることができれば、その手番のプレイヤーの勝ちとなります。
- 回文が作れず、文字を1つ削除しなければならない状況になったプレイヤーは、その時点では勝利できません。
両プレイヤーは常に最適な手を選ぶものとし、先手はプレイヤー1です。このとき、ゲームの勝者が誰になるかを求めるのが本記事のテーマです。
例えば、入力が「pqpppq」だった場合、答えは Player1 になります。プレイヤー1は最初の手番で文字を並べ替えて「ppqqpp」を作ればよいので、即座に勝利できるからです。
アルゴリズムの考え方
ここで重要になるのが回文の性質です。回文では、奇数回出現する文字は高々1種類しか存在できません(文字列の長さが奇数の場合の中央の1文字だけが例外です)。
そこで、各文字の出現回数を数え、「奇数回出現する文字の種類数」を count とすると、次のように勝敗を判定できます。
- count が 0 の場合:すべての文字が偶数回出現しているため、手番のプレイヤーはすぐに並べ替えて回文を作り、勝利できます。
- count が奇数の場合:手番のプレイヤーは文字を1つ削除することで count を偶数にできます。相手は必ず count を奇数に戻す操作を強いられ、最終的に回文を作れるタイミングを掴むのは先手側です。
以上のことから、count が 0 または奇数ならプレイヤー1の勝ち、偶数(かつ0以外)ならプレイヤー2の勝ちであることが分かります。
解法の手順
- l := 文字列の長さとする
- freq := サイズ26のリストを用意し、すべて0で初期化する(各アルファベットの出現回数を記録)
- 文字列を先頭から走査し、各文字の出現回数を freq に加算する
- count := 0 とし、freq の中で奇数になっている要素の個数を数える
- count が 0 または奇数であれば 1(プレイヤー1の勝ち)を返す
- それ以外の場合は 2(プレイヤー2の勝ち)を返す
実装例
それでは、以下のPythonコードで実際の動作を確認してみましょう。
def who_is_the_winner(sequence):
l = len(sequence)
freq = [0 for i in range(26)]
for i in range(0, l, 1):
freq[ord(sequence[i]) - ord('a')] += 1
count = 0
for i in range(26):
if (freq[i] % 2 != 0):
count += 1
if (count == 0 or count & 1 == 1):
return 1
else:
return 2
sequence = "pqpppq"
print("Player:", who_is_the_winner(sequence))
入力
"pqpppq"
出力
Player: 1
まとめ
この問題は、回文の「奇数回出現する文字は高々1種類」という性質を利用することで、文字列を1回走査するだけで O(n) の計算量で勝者を判定できます。ゲーム理論的な考察が必要に見えますが、結局は「奇数回出現する文字の種類数の偶奇」だけに着目すればよい、という点がポイントです。
-
【Python入門】文字列の先頭から100文字を取得する方法をわかりやすく解説
スライス演算子(:)を使った部分文字列の取得 Pythonでは、スライス演算子( : )を使用することで、文字列の一部分を簡単に取り出すことができます。スライス演算子には2つのオペランドがあり、それぞれ「切り出し開始位置のインデックス」と「終了位置のインデックス」を指定します。 基本的な構文は以下のとおりです。 substr = var[x:y] この場合、インデックス x から y の直前までの文字列が取得されます(y 自体は含まれない点に注意してください)。 具体例:7文字目から3文字を取り出す 次の例では、「Hello how are you?」という文字列から、7文字目以降の3文字を取
-
【初心者向け】Pythonで文字列の長さを取得する方法をわかりやすく解説
Pythonで文字列の長さを取得する基本:len()関数Pythonには、文字列やリスト、タプルといった複合オブジェクトの長さ(要素数)を取得できる組み込み関数 len() が用意されています。文字列の長さを知りたい場合は、対象の文字列をそのまま len() の引数として渡すだけでOKです。print(len(abcdefghijklmnopqrstuvwxyz))出力結果:26変数に格納した文字列の長さを取得する実際の開発では、変数に代入した文字列の長さを調べる場面が多いでしょう。次のように、変数を len() に渡すだけで簡単に取得できます。text = Hello, Python! pr