Check If a String Contains a Permutation of Another
Solve this Problems1 and s2, return true if s2 contains a contiguous substring that is a permutation (an anagram, rearranged) of s1.
A permutation doesn't care about order — only about having exactly the same letters, with exactly the same counts. That turns this from a string-matching problem into a counting problem: a window of s2 is a match precisely when its 26-letter frequency count equals s1's. Rebuilding that count for every window works, but the sliding windowSliding WindowMaintaining a running result over a contiguous range that grows or shrinks one element at a time, instead of recomputing the result for every range from scratch. technique keeps the count updated in O(1) per slide — bump the letter entering, drop the letter leaving — instead of recounting all of s1.length letters every time.
Test Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
1 ≤ s1.length ≤ s2.length ≤ 10⁴ - ◆
s1 and s2 consist of lowercase English letters only
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
| 1 | class Solution { |
| 2 | public boolean containsPermutation(String s1, String s2) { |
| 3 | int n = s1.length(), m = s2.length(); |
| 4 | if (n > m) return false; |
| 5 | int[] need = new int[26], window = new int[26]; |
| 6 | for (char c : s1.toCharArray()) need[c - 'a']++; |
| 7 | for (int i = 0; i < n; i++) window[s2.charAt(i) - 'a']++; |
| 8 | if (Arrays.equals(need, window)) return true; |
| 9 | for (int i = n; i < m; i++) { |
| 10 | window[s2.charAt(i) - 'a']++; |
| 11 | window[s2.charAt(i - n) - 'a']--; |
| 12 | if (Arrays.equals(need, window)) return true; |
| 13 | } |
| 14 | return false; |
| 15 | } |
| 16 | } |
| 17 |
"ei"Build the letter-count of s1's frequency array and the very first window of s2, "ei".
Approach & Solutions
Brute Force
BruteBuild a 26-letter frequency count for s1. Then for every window of length s1.length in s2, build that window's own frequency count from scratch and compare it against s1's. Correct, but rebuilding a fresh count for every single window throws away almost all of the work done for the window right before it.
O(n · 26)O(1)1class Solution {
2 public boolean containsPermutation(String s1, String s2) {
3 int n = s1.length();
4 if (n > s2.length()) return false;
5 int[] need = new int[26];
6 for (char c : s1.toCharArray()) need[c - 'a']++;
7 for (int i = 0; i <= s2.length() - n; i++) {
8 int[] window = new int[26];
9 for (int j = i; j < i + n; j++) window[s2.charAt(j) - 'a']++;
10 if (Arrays.equals(need, window)) return true;
11 }
12 return false;
13 }
14}Optimal — Sliding Window
OptimalBuild s1's 26-letter frequency count once, and the very first window's frequency count once. Compare them. Then slide the window one character at a time: bump the count of the character entering on the right, drop the count of the character leaving on the left, and compare again — no need to rebuild the whole window's count from scratch on every slide.
O(n)O(1)1class Solution {
2 public boolean containsPermutation(String s1, String s2) {
3 int n = s1.length(), m = s2.length();
4 if (n > m) return false;
5 int[] need = new int[26], window = new int[26];
6 for (char c : s1.toCharArray()) need[c - 'a']++;
7 for (int i = 0; i < n; i++) window[s2.charAt(i) - 'a']++;
8 if (Arrays.equals(need, window)) return true;
9 for (int i = n; i < m; i++) {
10 window[s2.charAt(i) - 'a']++;
11 window[s2.charAt(i - n) - 'a']--;
12 if (Arrays.equals(need, window)) return true;
13 }
14 return false;
15 }
16}