AlgoMaster Logo

Permutation in String

Last Updated: March 21, 2026

medium
5 min read

Understanding the Problem

At first glance, this looks like a permutation generation problem. You might think: generate all permutations of s1, then check if any of them appears as a substring in s2. That would work, but it's wildly inefficient.

The key observation is this: two strings are permutations of each other if and only if they have the exact same character frequencies. "ab" and "ba" are permutations because both contain one 'a' and one 'b'. So instead of generating permutations, we just need to find a contiguous substring of s2 with length equal to s1 that has the same character frequency distribution as s1.

That turns this into a sliding window problem. Slide a window of size len(s1) across s2, and at each position, check whether the window's character frequencies match s1's frequencies.

Key Constraints:

  • 1 <= s1.length, s2.length <= 10^4 → Both strings can have up to 10,000 characters. An O(n m) approach is borderline (10^8 operations), so we should aim for O(n + m) or O(26 m) at most. Anything involving factorial time (generating permutations) is completely off the table.
  • s1 and s2 consist of lowercase English letters → Only 26 possible characters. This means frequency arrays of size 26 are effectively O(1) space, and comparing two frequency arrays is O(26) = O(1).

Approach 1: Brute Force (Generate All Permutations)

Intuition

The most literal reading of the problem says "return true if one of s1's permutations is a substring of s2." So the brute force approach does exactly that: generate every permutation of s1, then check if any of them appears in s2 as a substring.

This is conceptually simple but catastrophically slow. The number of permutations of a string of length n is n!, which is 3,628,800 for n = 10 and grows astronomically from there. For the given constraint of n up to 10^4, this approach is completely impractical. But it's worth understanding as a starting point to see why we need a smarter approach.

Algorithm

  1. Generate all permutations of s1
  2. For each permutation, check if it appears as a substring in s2
  3. If any permutation is found in s2, return true
  4. If no permutation is a substring, return false

Code

The brute force generates n! permutations, which is astronomical for even modest string lengths. But every permutation of a string has the exact same character frequencies, so all that computation is redundant. What if we skipped permutation generation entirely and just compared character frequencies directly?

Approach 2: Sliding Window with Frequency Comparison

Intuition

Here's the key insight that changes everything: two strings are permutations of each other if and only if they have identical character frequencies. "abc" and "bca" both have exactly one 'a', one 'b', and one 'c'. So instead of checking every permutation, we just need to find a window of length len(s1) in s2 whose character frequency matches s1's frequency.

Since we're looking at a fixed-size window (always len(s1) characters), we can slide it across s2 one character at a time. When the window moves right, we add the new character entering the window and remove the character leaving the window. Then we compare the two frequency arrays.

With only 26 lowercase letters, comparing two frequency arrays is a constant-time operation. So the whole thing runs in O(26 * m) time, which is effectively O(m).

Algorithm

  1. If s1 is longer than s2, return false immediately (no window can fit)
  2. Build a frequency array for s1
  3. Build a frequency array for the first window of size len(s1) in s2
  4. Compare the two arrays. If they match, return true
  5. Slide the window one character at a time:
    • Add the new character entering from the right
    • Remove the character leaving from the left
    • Compare frequency arrays. If they match, return true
  6. If no window matched, return false

Code

This approach works well, but at every window position we compare all 26 entries of the two frequency arrays. When the window slides by one position, only two characters change. What if we tracked how many characters already have matching frequencies and updated that count incrementally?

Approach 3: Optimized Sliding Window with Match Count

Intuition

The previous approach compares all 26 frequency entries at every window position. But when the window slides by one, only two characters are affected: the one entering and the one leaving. So at most two frequency entries change. Comparing all 26 entries is overkill.

The optimization is to maintain a matches counter that tracks how many of the 26 characters currently have equal frequencies in s1 and the window. When matches == 26, every character frequency agrees, meaning the window is a permutation of s1.

When a character enters or leaves the window, we check whether that specific character's match status changed (from matching to not matching, or vice versa) and update the counter accordingly. This reduces the per-step work from O(26) to O(1).

Algorithm

  1. If s1 is longer than s2, return false
  2. Build frequency arrays for s1 and the first window of size len(s1) in s2
  3. Count how many of the 26 characters have equal frequencies (initialize matches)
  4. If matches == 26, return true
  5. Slide the window across s2. For each step:
    • Add the new character: increment its window frequency. Check if this change made it match or unmatch s1's frequency, and update matches
    • Remove the old character: decrement its window frequency. Same match/unmatch check
    • If matches == 26, return true
  6. Return false

Code