🐙

NeetCode 150 [Sliding Window]:medium 3/3

に公開

NeetCodeのSolutionを書いていく

Permutation in String

問題概要

文字列s1とs2が与えられます。
s2がs1の順列を含む場合はtrueをそれ以外であればfalseを返しましょう。
文字列はすべて小文字です。
順列とは「ある集合を並べ替えた時の並び」です。

Input: s1 = "abc", s2 = "lecabee"
Output: true

"cab"は"abc"の順列なのでtrue

Input: s1 = "abc", s2 = "lecaabee"
Output: false

制約

s1もs2も長さは1以上1000以下。
計算時間:O(n)
メモリ:O(1)

メモ

自分の回答。
うーむわかりにくい。

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        l = r = 0
        s1_dict: dict[str, int] = {}

        for c in s1:
            s1_dict[c] = s1_dict.get(c, 0) + 1
        s1_dict_copy = s1_dict.copy()

        r = -1
        while True:
            r += 1
            if r == len(s2):
                return False

            # l~r間がs1の長さを超えたらlをずらす必要がある
            if r - l + 1 > len(s1):
                # ずらすときは、文字カウンターを戻す
                if s2[l] in s1_dict:
                    s1_dict[s2[l]] += 1
                l += 1

            # rの文字がs1にあるかチェック
            v = s1_dict.get(s2[r])
            # ない場合は、その文字は使えないので、s1_dictをリセットして次に進む
            if v is None:
                s1_dict = s1_dict_copy.copy()
                l = r + 1  # この時lも次のrと同じ位置を指すようにする
                continue

            # ある場合で、既に文字列数を使い果たしている場合は
            if s1_dict[s2[r]] == 0:
                l += 1  # lをずらす必要がある
                s1_dict[s2[l]] += 1  # ずらした文字のカウントを戻す
                r -= 1  # rはもう一度評価するために一つ前に戻す
                continue

            # 文字カウントを減らす
            s1_dict[s2[r]] -= 1

            # 全部0になったらTrue
            if not any(s1_dict.values()):
                return True

chatgptの回答!
わかりやすい!

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        from collections import Counter
        len1, len2 = len(s1), len(s2)

        # 変更点1: s1 の長さが s2 より大きい場合は順列が存在しないので False を返す
        if len1 > len2:
            return False

        # 変更点2: s1 の文字カウントを Counter で取得
        count1 = Counter(s1)
        # 変更点3: s2 の最初のウィンドウ(長さ len1)のカウントを取得
        window_count = Counter(s2[:len1])

        # 変更点4: 初期ウィンドウで s1 のカウンターと一致するか検証
        if count1 == window_count:
            return True

        # 変更点5: ウィンドウをスライドさせながら更新
        for i in range(len1, len2):
            # 変更点6: ウィンドウに新たに加わる文字のカウントを更新
            window_count[s2[i]] += 1
            # 変更点7: ウィンドウから外れる文字のカウントをデクリメント
            window_count[s2[i - len1]] -= 1
            # 変更点8: カウントが 0 の場合はキーを削除してクリーンに保つ
            if window_count[s2[i - len1]] == 0:
                del window_count[s2[i - len1]]
            # 変更点9: 現在のウィンドウと s1 のカウンターが一致すれば順列が存在するので True を返す
            if count1 == window_count:
                return True

        # 変更点10: 一致するウィンドウがなければ False を返す
        return False

Discussion