スライディングウィンドウ法による効率的な部分文字列検索アルゴリズム
Minimum Window
Substring問題は、文字列sの中から文字列tのすべての文字を含む最小の部分文字列を見つける問題です。
この問題はスライディングウィンドウ法と2ポインタ法を組み合わせることで、O(m+n)の時間計算量で効率的に解くことができます。
文字列tの文字頻度をハッシュマップneedに記録し、現在のウィンドウの状態を追跡する変数を初期化します。
右ポインタを移動して文字を追加し、haveハッシュマップを更新。追加した文字が条件を満たすかチェックします。
formed == requiredの時、現在のウィンドウは有効。この状態でウィンドウの最小化を試みます。
左ポインタを移動してウィンドウを縮小。有効な間は最小ウィンドウを更新し、無効になったら拡張に戻ります。
from typing import Dict, Optional
from collections import defaultdict
class Solution:
def minWindow(self, s: str, t: str) -> str:
"""
最小ウィンドウ部分文字列を返す
Args:
s (str): 探索対象文字列
t (str): 必要文字列
Returns:
str: 条件を満たす最小部分文字列。存在しない場合は空文字。
"""
# 入力検証
if not isinstance(s, str) or not isinstance(t, str):
raise TypeError("Both s and t must be strings")
if not t:
raise ValueError("String t must not be empty")
if len(s) < len(t):
return ""
# t の文字頻度を記録
need: Dict[str, int] = defaultdict(int)
for ch in t:
need[ch] += 1
# ウィンドウ状態の管理
have: Dict[str, int] = defaultdict(int)
required = len(need) # 必要な文字種類数
formed = 0 # 現在満たしている文字種類数
res: Optional[tuple[int, int]] = None # 結果のインデックス
l = 0 # 左ポインタ
# スライディングウィンドウ
for r, ch in enumerate(s):
# ウィンドウを右に拡張
have[ch] += 1
if ch in need and have[ch] == need[ch]:
formed += 1
# 有効なウィンドウの間、左端を縮める
while formed == required:
# 最小ウィンドウを更新
if res is None or (r - l) < (res[1] - res[0]):
res = (l, r)
# 左端の文字を除去
left_ch = s[l]
have[left_ch] -= 1
if left_ch in need and have[left_ch] < need[left_ch]:
formed -= 1
l += 1
return "" if res is None else s[res[0]:res[1]+1]
def minWindow_optimized(s: str, t: str) -> str:
"""競技プログラミング向け高速版"""
need = defaultdict(int)
for ch in t:
need[ch] += 1
have = defaultdict(int)
required, formed = len(need), 0
res, l = None, 0
for r, ch in enumerate(s):
have[ch] += 1
if ch in need and have[ch] == need[ch]:
formed += 1
while formed == required:
if res is None or (r - l) < (res[1] - res[0]):
res = (l, r)
left_ch = s[l]
have[left_ch] -= 1
if left_ch in need and have[left_ch] < need[left_ch]:
formed -= 1
l += 1
return "" if res is None else s[res[0]:res[1]+1]
各文字は最大2回処理される(右ポインタと左ポインタで各1回)
英字のみの制約により、ハッシュマップのサイズは最大52で定数
文字種類数での判定:
全文字頻度を毎回比較するのではなく、formed変数で満たした文字種類数を追跡することで高速化