Permutation in String
Does s2 contain any permutation of s1? Fixed-size sliding window over a frequency vector.
4 min read
sliding-window string
Problem#
Return true iff s2 contains a substring that is a permutation of s1.
Examples#
s1 = "ab", s2 = "eidbaooo"→true("ba")s1 = "ab", s2 = "eidboaoo"→false
Constraints#
1 <= s1.length, s2.length <= 10^4, lowercase.
Approach#
Fixed-length window equal to s1.size(). Maintain a 26-int frequency vector for the window in s2; compare to s1’s frequency. Each shift updates two slots.
Solution#
class Solution {public: bool checkInclusion(string s1, string s2) { int n = s1.size(), m = s2.size(); if (n > m) return false; int a[26] = {0}, b[26] = {0}; for (int i = 0; i < n; ++i) { ++a[s1[i] - 'a']; ++b[s2[i] - 'a']; } if (memcmp(a, b, sizeof a) == 0) return true; for (int i = n; i < m; ++i) { ++b[s2[i] - 'a']; --b[s2[i - n] - 'a']; if (memcmp(a, b, sizeof a) == 0) return true; } return false; }};func checkInclusion(s1 string, s2 string) bool { n, m := len(s1), len(s2) if n > m { return false } var a, b [26]int for i := 0; i < n; i++ { a[s1[i]-'a']++ b[s2[i]-'a']++ } if a == b { return true } for i := n; i < m; i++ { b[s2[i]-'a']++ b[s2[i-n]-'a']-- if a == b { return true } } return false}class Solution: def checkInclusion(self, s1: str, s2: str) -> bool: n, m = len(s1), len(s2) if n > m: return False a = [0] * 26 b = [0] * 26 for i in range(n): a[ord(s1[i]) - ord('a')] += 1 b[ord(s2[i]) - ord('a')] += 1 if a == b: return True for i in range(n, m): b[ord(s2[i]) - ord('a')] += 1 b[ord(s2[i - n]) - ord('a')] -= 1 if a == b: return True return Falsefunction checkInclusion(s1, s2) { const n = s1.length, m = s2.length; if (n > m) return false; const a = new Array(26).fill(0); const b = new Array(26).fill(0); const A = 'a'.charCodeAt(0); for (let i = 0; i < n; i++) { a[s1.charCodeAt(i) - A]++; b[s2.charCodeAt(i) - A]++; } const equal = () => { for (let i = 0; i < 26; i++) if (a[i] !== b[i]) return false; return true; }; if (equal()) return true; for (let i = n; i < m; i++) { b[s2.charCodeAt(i) - A]++; b[s2.charCodeAt(i - n) - A]--; if (equal()) return true; } return false;}class Solution { public boolean checkInclusion(String s1, String s2) { int n = s1.length(), m = s2.length(); if (n > m) return false; int[] a = new int[26]; int[] b = new int[26]; for (int i = 0; i < n; i++) { a[s1.charAt(i) - 'a']++; b[s2.charAt(i) - 'a']++; } if (Arrays.equals(a, b)) return true; for (int i = n; i < m; i++) { b[s2.charAt(i) - 'a']++; b[s2.charAt(i - n) - 'a']--; if (Arrays.equals(a, b)) return true; } return false; }}function checkInclusion(s1: string, s2: string): boolean { const n = s1.length, m = s2.length; if (n > m) return false; const a: number[] = new Array(26).fill(0); const b: number[] = new Array(26).fill(0); const A = 'a'.charCodeAt(0); for (let i = 0; i < n; i++) { a[s1.charCodeAt(i) - A]++; b[s2.charCodeAt(i) - A]++; } const equal = (): boolean => { for (let i = 0; i < 26; i++) if (a[i] !== b[i]) return false; return true; }; if (equal()) return true; for (let i = n; i < m; i++) { b[s2.charCodeAt(i) - A]++; b[s2.charCodeAt(i - n) - A]--; if (equal()) return true; } return false;}Editorial#
Each window comparison is O(26) = O(1), and the window updates are also constant. Total O(n). The memcmp over fixed-size buffers compiles to a couple of SIMD instructions on modern compilers — beats element-wise comparison.
Complexity#
- Time: O(n + m).
- Space: O(1).
Concept revision#
Related#