LeetCode 567: Permutation in String Solution in Python – A Step-by-Step Guide

Imagine you’re given two strings—like "ab" and "eidbaooo"—and your task is to figure out if the longer string has a chunk somewhere that could be rearranged to match the shorter one, such as finding "ba" within "eidbaooo" as a permutation of "ab." That’s the captivating challenge of LeetCode 567: Permutation in String, a medium-level problem that’s a fantastic way to practice string manipulation in Python. We’ll explore two solutions: the Best Solution, a sliding window with frequency counting that’s efficient and clever, and an Alternative Solution, a brute-force permutation checking that’s thorough but slower. With detailed examples, clear code, and a friendly tone—especially for the sliding window approach—this guide will help you spot that permutation, whether you’re new to coding or leveling up. Let’s slide those windows and start matching!

What Is LeetCode 567: Permutation in String?

Section link icon

In LeetCode 567: Permutation in String, you’re given two strings s1 and s2, and your task is to return True if s2 contains a substring that is a permutation of s1—meaning it has the same characters with the same frequencies, in any order—and False otherwise. For example, with s1 = "ab" and s2 = "eidbaooo", s2 contains "ba," a permutation of "ab," so return True. This problem builds on LeetCode 438: Find All Anagrams in a String for substring matching but focuses on finding just one valid permutation.

Problem Statement

  • Input: s1 (str)—target string; s2 (str)—source string to search in.
  • Output: Boolean—True if s2 contains a permutation of s1, False otherwise.
  • Rules: Permutation means same character counts; substring must be contiguous.

Constraints

  • 1 <= s1.length, s2.length <= 10⁴
  • s1 and s2 consist of lowercase English letters.

Examples

  • Input: s1 = "ab", s2 = "eidbaooo"
    • Output: True
    • Substring "ba" in "eidbaooo" is a permutation of "ab".
  • Input: s1 = "ab", s2 = "eidboaoo"
    • Output: False
    • No contiguous substring matches "ab"’s frequency.
  • Input: s1 = "hello", s2 = "ooolleoooeh"
    • Output: False
    • No substring has "hello"’s exact character count.

Understanding the Problem: Finding a Permutation

Section link icon

To solve LeetCode 567: Permutation in String in Python, we need to determine if s2 contains a contiguous substring of the same length as s1 with identical character frequencies, indicating it’s a permutation of s1. With lengths up to 10⁴, a brute-force approach generating all permutations of s1 and searching for each (O(n! * m)) is impractical. Instead, we can use a sliding window to efficiently compare frequency counts in O(m) time, where m is s2’s length. We’ll explore:

  • Best Solution (Sliding Window with Frequency Counting): O(m) time, O(1) space—fast and optimal.
  • Alternative Solution (Brute-Force Permutation Checking): O(n! * m) time, O(n) space—simple but slow (n = s1 length, m = s2 length).

Let’s dive into the sliding window solution with a friendly breakdown!

Best Solution: Sliding Window with Frequency Counting

Section link icon

Why Sliding Window Wins

The sliding window with frequency counting is the best for LeetCode 567 because it efficiently checks for a permutation in O(m) time and O(1) space (since the alphabet is fixed at 26 lowercase letters) by sliding a fixed-size window over s2, maintaining character frequencies, and comparing them to s1’s frequencies in constant time per step. It’s like sliding a magnifying glass over the string, checking each section for a perfect character match—all in one swift pass!

How It Works

Think of this as a window-sliding detective:

  • Step 1: Initialize Frequencies:
    • Create frequency arrays (or dictionaries) for s1 and the first window of s2 (size = s1 length).
  • Step 2: Check First Window:
    • Compare frequencies; if match, return True.
  • Step 3: Slide Window:
    • For each step:
      • Remove leftmost character (decrement its count).
      • Add new rightmost character (increment its count).
      • Check if frequencies match s1’s.
  • Step 4: Return Result:
    • True if match found, False if end reached without match.
  • Why It Works:
    • Permutations have identical frequency counts.
    • Sliding window avoids recomputing full counts.

It’s like a frequency-matching sleuth!

Step-by-Step Example

Example: s1 = "ab", s2 = "eidbaooo"

  • Step 1: Initialize:
    • s1 freq: {'a': 1, 'b': 1}.
    • Window (0-1): "ei" → {'e': 1, 'i': 1}.
  • Step 2: First window mismatch.
  • Step 3: Slide window:
    • (1-2): Remove 'e', add 'd': "id" → {'i': 1, 'd': 1}, mismatch.
    • (2-3): Remove 'i', add 'b': "db" → {'d': 1, 'b': 1}, mismatch.
    • (3-4): Remove 'd', add 'a': "ba" → {'b': 1, 'a': 1}, match! Return True.
  • Result: True.

Example: s1 = "ab", s2 = "eidboaoo"

  • Step 1: s1 freq: {'a': 1, 'b': 1}.
  • Step 2: Window "ei": {'e': 1, 'i': 1}, mismatch.
  • Step 3: Slide:
    • "id": {'i': 1, 'd': 1}, mismatch.
    • "db": {'d': 1, 'b': 1}, mismatch.
    • "bo": {'b': 1, 'o': 1}, mismatch.
    • "oa": {'o': 1, 'a': 1}, mismatch.
    • "ao": {'a': 1, 'o': 1}, mismatch.
    • "oo": {'o': 2}, mismatch.
  • Step 4: No match, end reached.
  • Result: False.

Code with Detailed Line-by-Line Explanation

Here’s the Python code, explained for beginners:

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        # Step 1: Check if s1 is longer than s2
        if len(s1) > len(s2):
            return False

        # Step 2: Initialize frequency arrays (26 lowercase letters)
        s1_count = [0] * 26
        window_count = [0] * 26
        for char in s1:
            s1_count[ord(char) - ord('a')] += 1

        # Step 3: Fill first window
        for i in range(len(s1)):
            window_count[ord(s2[i]) - ord('a')] += 1
        if s1_count == window_count:
            return True

        # Step 4: Slide window and check
        for i in range(len(s1), len(s2)):
            # Remove leftmost character
            window_count[ord(s2[i - len(s1)]) - ord('a')] -= 1
            # Add new rightmost character
            window_count[ord(s2[i]) - ord('a')] += 1
            if s1_count == window_count:
                return True

        # Step 5: No match found
        return False
  • Lines 4-5: If s1 longer than s2, impossible.
  • Lines 8-11: Count s1 frequencies (a-z).
  • Lines 14-17: Count first window, check match.
  • Lines 20-25: Slide window:
    • Decrement left character.
    • Increment right character.
    • Check match.
  • Line 28: Return False if no match.
  • Time Complexity: O(m)—single pass over s2.
  • Space Complexity: O(1)—fixed 26-letter arrays.

It’s like a window-sliding matchmaker!

Alternative Solution: Brute-Force Permutation Checking

Section link icon

Why an Alternative Approach?

The brute-force permutation checking generates all permutations of s1 and searches for each in s2, running in O(n! * m) time and O(n) space (n = s1 length, m = s2 length). It’s simple but impractical for larger strings, making it a good baseline for small inputs or understanding.

How It Works

Picture this as a permutation-hunting seeker:

  • Step 1: Generate all permutations of s1.
  • Step 2: Check if each is a substring of s2.
  • Step 3: Return True if any found, False otherwise.

It’s like a permutation-probing explorer!

Step-by-Step Example

Example: s1 = "ab", s2 = "eidbaooo"

  • Step 1: Permutations: ["ab", "ba"].
  • Step 2: Check:
    • "ab" in "eidbaooo" → False.
    • "ba" in "eidbaooo" → True.
  • Result: True.

Code for Brute-Force Approach

from itertools import permutations

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        if len(s1) > len(s2):
            return False

        # Generate all permutations of s1
        perms = [''.join(p) for p in permutations(s1)]

        # Check each permutation in s2
        for perm in perms:
            if perm in s2:
                return True

        return False
  • Lines 5-6: Check length feasibility.
  • Line 9: Generate permutations.
  • Lines 12-14: Check each as substring.
  • Line 16: Return False if no match.
  • Time Complexity: O(n! * m)—permutations times substring check.
  • Space Complexity: O(n)—permutation storage.

It’s a brute-force permutation checker!

Comparing the Two Solutions

Section link icon
  • Sliding Window (Best):
    • Pros: O(m), O(1), fast.
    • Cons: Frequency logic.
  • Brute-Force (Alternative):
    • Pros: O(n! * m), O(n), simple.
    • Cons: Impractical for large n.

Sliding window wins for efficiency!

Additional Examples and Edge Cases

Section link icon
  • s1 = "a", s2 = "a": True`.
  • s1 = "abc", s2 = "cba": True`.
  • s1 = "abc", s2 = "def": False`.

Sliding window handles them all!

Complexity Recap

Section link icon
  • Sliding Window: Time O(m), Space O(1).
  • Brute-Force: Time O(n! * m), Space O(n).

Sliding window’s the speed champ!

Key Takeaways

Section link icon
  • Sliding Window: Frequency finesse—learn at Python Basics!
  • Brute-Force: Permutation probe.
  • Strings: Permutations are fun.
  • Python: Slide or brute, your pick!

Final Thoughts: Spot That Permutation!

Section link icon

LeetCode 567: Permutation in String in Python is a clever string challenge. Sliding window with frequency counting is your fast track, while brute-force offers a thorough dive. Want more? Try LeetCode 438: Find All Anagrams or LeetCode 76: Minimum Window Substring. Ready to match? Head to Solve LeetCode 567 on LeetCode and find that permutation today!