Skip to content

Commit

Permalink
refactor(string): deconstruct and optimize sliding-window/
Browse files Browse the repository at this point in the history
  • Loading branch information
RayJune committed Jul 30, 2024
1 parent 7c1caea commit 25cd163
Show file tree
Hide file tree
Showing 14 changed files with 84 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,25 @@
* s consists of English letters.
*
* https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/
*
* @related 3-longest-substring-without-repeating-characters
* @related 340-longest-substring-with-at-most-k-distinct-characters
*/

/**
* 输入一个字符串 s,返回该至多包含两个不同字符的最长子串的长度
*
* 思路:
* Sliding Window + Hash Map
* 1. 创建一个 Hash Map 储存字符在滑动窗口中出现的次数,创建左右指针 left right 代表当前窗口的头尾
* 2. 更新 s[right] 在滑动窗口中出现的次数到 map 中
* 3. 如果当前 map 出现了第三个不重复的字符,边把 left 节点右移边更新 map 中 s[left] -= 1,直到 map[s[left]] === 0 删除节点,从而达到 map.size === 2 的要求
* 4. 更新最大长度值,right 节点右移一位
* 5. 重复步骤 2 3 4 直到遍历完毕
*
* Time Complexity: O(n) = 遍历次数
* Space complexity: O(1)
* Auxiliary complexity: O(1)
* Time Complexity: O(n) = 左右指针遍历次数
* Space Complexity: O(1)
* Auxiliary Complexity: O(1)
* 其中 n 是字符串 s 的长度
*
* @param {string} s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,25 @@
* s consists of English letters.
*
* https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/
*
* @related 3-longest-substring-without-repeating-characters
* @related 340-longest-substring-with-at-most-k-distinct-characters
* @related 146-lru-cache
*/

/**
* Sliding Window + Ordered Dictionary
* 输入一个字符串 s,返回该至多包含两个不同字符的最长子串的长度
*
* 思路:
* Sliding Window + Linked Hash Map
* 利用 JS 中的 Map 是有序的特性来简化操作
* 1. 创建一个 Hash Map 来存储在元素在 s 中的最大下标值,创建左右指针 left right 代表当前窗口的头尾
* 2. 更新 s[right] 值到 map 中
* 3. 如果当前 map 出现了第三个不重复的字符,利用 Map 是有序的特性通过 map.entries().next() 拿到 map 头部元素的 key 和 value,删除 map 中的 key 节点并移动 left 节点到 value + 1 即可正确更新滑动窗口的头部
* 4. 更新最大长度,right 节点右移一位
* 5. 重复步骤 2 3 4 直到遍历完毕
*
* Time Complexity: O(n) = 遍历次数
* Time Complexity: O(n) = 左右指针遍历次数
* Space complexity: O(1)
* Auxiliary complexity: O(1)
* 其中 n 是字符串 s 的长度
Expand All @@ -38,9 +51,7 @@ function lengthOfLongestSubstringTwoDistinct(s) {
let maxLen = 0;

while (right < s.length) {
if (map.has(s[right])) {
map.delete(s[right]);
}
map.delete(s[right]);
map.set(s[right], right);
if (map.size > 2) {
const [key, value] = map.entries().next().value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ test('s = "ccaabbb"', () => {
test('s = "cdaba"', () => {
expect(lengthOfLongestSubstringTwoDistinct('cdaba')).toBe(3);
});

test('s = "abbbaccccc"', () => {
expect(lengthOfLongestSubstringTwoDistinct('abbbaccccc')).toBe(6);
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,46 @@
* s consists of English letters, digits, symbols and spaces.
*
* https://leetcode.com/problems/longest-substring-without-repeating-characters/
*
* @related 159-longest-substring-with-at-most-two-distinct-characters
* @related 340-longest-substring-with-at-most-k-distinct-characters
*/

/**
* 输入一个字符串 s,返回其不含有重复字符的最长子串的长度
*
* 思路:
* Sliding Window + Hash Set
* 1. 创建一个 Hash Set,储存当前子串的字符,创建左右指针 left right 代表当前窗口的头尾
* 2. 遍历字符串 s 一直到出现重复字符或遍历完 s
* 3. 把当前的窗口长度与之前的最大长度作比较,更新最大长度值
* 4. 如果出现重复元素,则不断从 Set 里删除 left 节点来去除重复值,更新 left 值
* 5. 重复步骤 2 3 4 直到遍历完毕
*
* Time Complexity: O(n) = 遍历次数
* Space complexity: O(min(n, s)) = set 长度
* Auxiliary complexity: O(min(n, s)) = set 长度
* 其中 n 是字符串 s 的长度,s 是字符集大小(字符串中可能出现的字符)
* Time Complexity: O(n) = 左右指针遍历次数
* Space Complexity: O(m) = set 长度
* Auxiliary Complexity: O(m) = set 长度
* 其中 n 是字符串 s 的长度,m 是字符集大小(字符串中可能出现的字符)
*
* @param {string} s
* @returns {number}
*/
function lengthOfLongestSubstring(s) {
const set = new Set();
const len = s.length;
let left = 0;
let right = 0;
let maxLen = 0;

while (right < len) {
while (!set.has(s[right]) && right < len) {
while (right < s.length) {
while (right < s.length && !set.has(s[right])) {
set.add(s[right]);
right += 1;
}
maxLen = Math.max(maxLen, right - left);
set.delete(s[left]);
left += 1;
while (set.has(s[right])) {
set.delete(s[left]);
left += 1;
}
}

return maxLen;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,23 @@
* 0 <= k <= 50
*
* https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/
*
* @related 3-longest-substring-without-repeating-characters
* @related 159-longest-substring-with-at-most-two-distinct-characters
*/

/**
* 输入一个字符串 s 和数字 k,返回该至多包含 k 个不同字符的最长子串的长度
*
* 思路:
* Sliding Window + Hash Map
* 1. 创建一个 Hash Map 储存字符在滑动窗口中出现的次数,创建左右指针 left right 代表当前窗口的头尾
* 2. 更新 s[right] 在滑动窗口中出现的次数到 map 中
* 3. 如果当前 map 出现了第 k 个不重复的字符,边把 left 节点右移边更新 map 中 s[left] -= 1,直到 map[s[left]] === 0 删除节点,从而达到 map.size === k 的要求
* 4. 更新最大长度值,right 节点右移一位
* 5. 重复步骤 2 3 4 直到遍历完毕
*
* Time Complexity: O(n * k) = 遍历次数
* Time Complexity: O(n) = 左右指针遍历次数
* Space complexity: O(k) = map 长度
* Auxiliary complexity: O(k) = map
* 其中 n 为字符串 s 的长度
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,28 @@
* 0 <= k <= 50
*
* https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/
*
* @related 3-longest-substring-without-repeating-characters
* @related 159-longest-substring-with-at-most-two-distinct-characters
* @related 146-lru-cache
*/

/**
* Sliding Window + Ordered Dictionary
* 输入一个字符串 s,返回该至多包含 k 个不同字符的最长子串的长度
*
* 思路:
* Sliding Window + Linked Hash Map
* 利用 JS 中的 Map 是有序的特性来简化操作
* 1. 创建一个 Hash Map 来存储在元素在 s 中的最大下标值,创建左右指针 left right 代表当前窗口的头尾
* 2. 更新 s[right] 值到 map 中
* 3. 如果当前 map 出现了第 k 个不重复的字符,利用 Map 是有序的特性通过 map.entries().next() 拿到 map 头部元素的 key 和 value,删除 map 中的 key 节点并移动 left 节点到 value + 1 即可正确更新滑动窗口的头部
* 4. 更新最大长度,right 节点右移一位
* 5. 重复步骤 2 3 4 直到遍历完毕
*
* Time Complexity: O(n) = while 循环次数
* Time Complexity: O(n) = 左右指针遍历次数
* Space complexity: O(k) = map 长度
* Auxiliary complexity: O(k) = map 长度
* 其中 n 为字符串 s 的长度
* 其中 n 是字符串 s 的长度
*
* @param {string} s
* @param {number} k
Expand All @@ -39,9 +52,7 @@ function lengthOfLongestSubstringKDistinct(s, k) {
let maxLen = 0;

while (right < s.length) {
if (map.has(s[right])) {
map.delete(s[right]);
}
map.delete(s[right]);
map.set(s[right], right);
if (map.size > k) {
const [key, value] = map.entries().next().value;
Expand Down

0 comments on commit 25cd163

Please sign in to comment.