Sweaty Imposter

Permutation in String

MediumSliding WindowNeetCode 150MidOpen on LeetCodeUpdated

You're given two strings s1 and s2 of lowercase English letters. Return true if s2 contains a substring that is a rearrangement of s1: exactly the same letters with the same counts, in any order. The match must be contiguous and exactly as long as s1. Return false otherwise, including when s1 is longer than s2.

Examples

s1 = "ab", s2 = "eidbaooo" returns true

e0i1d2b3a4o5o6o7
"ba" is a rearrangement of "ab".

The substring "ba" at indices 3 and 4 has one a and one b, the same letters as s1 in a different order.

s1 = "ab", s2 = "eidboaoo" returns false

The length-2 substrings are "ei", "id", "db", "bo", "oa", "ao" and "oo". None is "ab" or "ba", because an o sits between the b and the a.

Constraints

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

Hints

Hint 1

A permutation has the same character counts, what stays constant as you scan?

Hint 2
A pointer toward the approach
Hint 3
The full approach, step by step

Drill This: Your Approach First

Approach

Two strings are rearrangements of each other exactly when their letter counts match. Build 26-slot counts for s1 and for the first len(s1) letters of s2, then slide a fixed-length window along s2: add the letter entering on the right, remove the one leaving on the left, and compare the two count arrays after each step. Each comparison costs 26 operations, a constant.

Time

O(|s1| + |s2|)

Space

O(1)

Pitfall

Check the first window before you start sliding, or you miss a match at the very start of s2 (s1 = "ab", s2 = "ba"). Also return false early when s1 is longer than s2, or building the first window reads past the end of s2.

Solutions

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        if (s1.length() > s2.length()) return false;
        int[] need = new int[26], have = new int[26];
        for (int i = 0; i < s1.length(); i++) {
            need[s1.charAt(i) - 'a']++;
            have[s2.charAt(i) - 'a']++;
        }
        if (Arrays.equals(need, have)) return true;
        for (int i = s1.length(); i < s2.length(); i++) {
            have[s2.charAt(i) - 'a']++;
            have[s2.charAt(i - s1.length()) - 'a']--;
            if (Arrays.equals(need, have)) return true;
        }
        return false;
    }
}
Drill This