Pythonで文字列の各インデックスから文字cまでの最短距離を求めるリストを作る方法
問題の概要
文字列 s と文字 c が与えられます(c は必ず s 内に存在するものとします)。ここで、s と同じ長さのリストを作成し、その各インデックス i の値を「s[i] から最も近い c までの距離」に設定します。
たとえば、入力が s = "ppqppq"、c = "q" の場合、出力は [2, 1, 0, 1, 1, 0] となります。
出力の内訳
- インデックス 0(p):最寄りの「q」はインデックス 2 → 距離 2
- インデックス 1(p):最寄りの「q」はインデックス 2 → 距離 1
- インデックス 2(q):自身が「q」→ 距離 0
- インデックス 3(p):最寄りの「q」はインデックス 2 → 距離 1
- インデックス 4(p):最寄りの「q」はインデックス 5 → 距離 1
- インデックス 5(q):自身が「q」→ 距離 0
解法の考え方
この問題は、次の手順で解きます。
- 変数
jに文字列sの長さを代入します。 - リスト
dを、すべての要素をj - 1(発生しうる最大距離)で初期化して作成します。 - 変数
xに、sの中で最初にcが出現するインデックスを代入します。 iを 0 からj - 1まで順に処理します。s[i]がcと一致し、かつi > xの場合は、xをiに更新し、indを 1 として左側の位置を遡って更新します。d[x - ind]がindより大きい間だけ値をindに書き換え、そうでなくなった時点でループを抜けます。- その後、
d[i]に|x - i|(直近のcまでの絶対距離)を代入します。
- 最後にリスト
dを返します。
ポイントは「直前に見つけた c の位置 x」を常に追跡することです。新しい c を発見したタイミングで、それより左側の位置に対して「新しい c の方が近い場合のみ」距離を更新していく仕組みになっています。
Pythonでの実装例
理解を深めるために、実際の実装を見てみましょう。
def solve(s, c):
j = len(s)
d = [j - 1] * j
x = s.index(c)
for i in range(j):
if s[i] == c and i > x:
x = i
ind = 1
while True:
if d[x - ind] > ind:
d[x - ind] = ind
else:
break
ind += 1
d[i] = abs(x - i)
return d
s = "ppqppq"
c = "q"
print(solve(s, c))
入力
"ppqppq", "q"
出力
[2, 1, 0, 1, 1, 0]
より読みやすい別解:左右2回の走査
上記の実装は正しく動作しますが、条件分岐が多くやや複雑です。実務では「左から右」「右から左」の2回の走査で求める方法がシンプルでおすすめです。
def solve(s, c):
n = len(s)
INF = float("inf")
d = [INF] * n
# 左から右へ走査し、左側の最寄りの c までの距離を記録
prev = INF
for i in range(n):
if s[i] == c:
prev = i
d[i] = min(d[i], abs(i - prev))
# 右から左へ走査し、右側の最寄りの c との距離と比較
prev = INF
for i in range(n - 1, -1, -1):
if s[i] == c:
prev = i
d[i] = min(d[i], abs(prev - i))
return d
print(solve("ppqppq", "q")) # [2, 1, 0, 1, 1, 0]
どちらの方法も計算量は O(n) ですが、2回の走査を使う方法はロジックの意図が明確になり、バグも生じにくくなります。
まとめ
文字列内の各位置から特定の文字までの最短距離を求める問題は、「直近の出現位置を追跡する」方法でも「両方向からの走査で最小値を取る」方法でも、線形時間 O(n) で解けます。面接や競技プログラミングでも頻出のパターンなので、ぜひマスターしておきましょう。
-
Pythonで文字列リストの最長共通プレフィックス(接頭辞)を求めるプログラム
小文字で構成された文字列のリストが与えられたとき、その中に共通して含まれる最長の共通プレフィックス(接頭辞)を見つける問題を考えてみましょう。例えば、入力が [antivirus, anticlockwise, antigravity] の場合、すべての文字列に共通する先頭部分は anti なので、出力は anti となります。解決のためのアプローチこの問題は、以下の手順で解くことができます。まず、リスト words をアルファベット順にソートします。これにより、辞書順で最も近い文字列同士が隣り合うため、比較が効率的になります。共通プレフィックスを格納するための新しいリスト prefix を用
-
PythonでリストからN個の最大要素を取得する方法
整数のリストが与えられたとき、その中からN個の大きな要素を取り出して新しいリストとして返すのが、ここでの課題です。本記事では、基本的なループ処理による方法から、Python標準ライブラリを活用した効率的な方法まで、サンプルコードとともに解説します。 例 入力 : [40, 5, 10, 20, 9] N = 2 出力 : [40, 20] アルゴリズム 整数のリストと、取得する要素数Nを受け取ります。 N回のループを実行します。 各ループでリスト内の最大値を探し、新しいリストに格納すると同時に元のリストから削除します。 実装コード def Nnumberele(list1, N):