題目描述:請(qǐng)從字符串中找出一個(gè)最長(zhǎng)的不包含重復(fù)字符的子字符串,計(jì)算該最長(zhǎng)子字符串的長(zhǎng)度。
題目分析
留意最長(zhǎng)子串和子序列不是一個(gè)概念。例如對(duì)“pwwkew”來說,最長(zhǎng)子串是“wke”,“pwke”是其中一個(gè)子序列。
在不考慮時(shí)間的情況下,直接暴力法對(duì)所有的子串進(jìn)行檢查。復(fù)雜度是,會(huì)超時(shí)錯(cuò)誤。
解法 1: 滑動(dòng)窗口
準(zhǔn)備 2 個(gè)指針 i、j,i 指向窗口左邊,j 指向右邊。指針每次可以向前“滑動(dòng)”一個(gè)位置,它們之間的區(qū)域就是“窗口”。
整體流程如下:
- 準(zhǔn)備哈希表 map。key 是 char,value 是 boolean,代表字符 char 是否出現(xiàn)在滑動(dòng)窗口內(nèi)
- i 和 j 初始化為 0,結(jié)果 ans 初始化為 0
- 檢查
s[j]
是否出現(xiàn)過:- 沒有出現(xiàn)過,擴(kuò)大窗口:記錄
s[j]
,指針 j 向右滑動(dòng)一格,更新 ans - 出現(xiàn)過,縮小窗口:指針 i 向右移動(dòng)一格,
map[s[i]]
更新為 false
- 沒有出現(xiàn)過,擴(kuò)大窗口:記錄
- 如果 i 和 j 沒有越界,回到 step3,否則返回 ans
代碼實(shí)現(xiàn)如下:
// ac地址:https://leetcode-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/
// 原文地址:https://xxoo521.com/2020-03-11-length-of-substring/
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
const length = s.length;
const map = {}; // char => boolean 代表著char是否在目前的窗口內(nèi)
let i = 0,
j = 0;
let ans = 0;
while (i < length && j < length) {
if (!map[s[j]]) {
ans = Math.max(j - i + 1, ans);
map[s[j]] = true;
++j;
} else {
// 如果char重復(fù),那么縮小滑動(dòng)窗口,并更新對(duì)應(yīng)的map
map[s[i]] = false;
++i;
}
}
return ans;
};
由于整個(gè)過程就是“推著”滑動(dòng)窗口從左到右,時(shí)間復(fù)雜度是,空間復(fù)雜度是
。
解法 2: 優(yōu)化后的滑動(dòng)窗口
在解法 1 的流程中的第 3 步,如果s[j]
出現(xiàn)在滑動(dòng)窗口內(nèi),采用的做法是左邊逐步縮小滑動(dòng)窗口。事實(shí)上,不需要逐步縮小。假設(shè)滑動(dòng)窗口內(nèi)和s[j]
相同字符下標(biāo)是 j',那么直接跳過[i, j']
范圍即可。
為了做到“跳動(dòng)優(yōu)化”,需要改造一下對(duì)哈希表 map 的用法:key 還是 char;value 變?yōu)?int,記錄 char 對(duì)應(yīng)的下標(biāo)。
整體代碼實(shí)現(xiàn)如下:
// ac地址:https://leetcode-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/
// 原文地址:https://xxoo521.com/2020-03-11-length-of-substring/
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
const length = s.length;
const map = new Map();
let i = 0,
j = 0;
let ans = 0;
while (i < length && j < length) {
// 容易理解:檢查s[j]是否出現(xiàn)過,并且s[j]重復(fù)的字符是否在當(dāng)前的滑動(dòng)窗口中
if (map.has(s[j]) && map.get(s[j]) >= i) {
i = map.get(s[j]) + 1;
}
ans = Math.max(j - i + 1, ans);
map.set(s[j], j);
++j;
}
return ans;
};
更多資料
整理不易,若對(duì)您有幫助,請(qǐng)給個(gè)「關(guān)注+點(diǎn)贊」,您的支持是我更新的動(dòng)力 ??
- ??Blog:劍指 Offer 題解 + JS 代碼
- ??Github :https://github.com/dongyuanxin/blog
- ?? 公眾號(hào):心譚博客