1. 題目來源
鏈接:最長不含重複字符的子字符串
來源:LeetCode——《劍指-Offer》專項
2. 題目說明
給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。
示例1:
輸入: “abcabcbb”
輸出: 3
解釋: 因爲無重複字符的最長子串是 “abc”,所以其長度爲 3。
示例2:
輸入: “bbbbb”
輸出: 1
解釋: 因爲無重複字符的最長子串是 “b”,所以其長度爲 1。
示例3:
輸入: “pwwkew”
輸出: 3
解釋: 因爲無重複字符的最長子串是 “wke”,所以其長度爲 3。請注意,你的答案必須是 子串 的長度,“pwke” 是一個子序列,不是子串。
提示:
s.length <= 40000
3. 題目解析
方法一:滑動窗口+HashMap+常規解法
這道題字符出現的位置很重要,所以可以使用 HashMap
來建立字符和其出現位置之間的映射。主要思路如下:
-
維護了一個滑動窗口,窗口內的都是沒有重複的字符,需要儘可能的擴大窗口的大小
-
由於窗口在不停向右滑動,所以只關心每個字符最後出現的位置,並建立映射
-
窗口的右邊界就是當前遍歷到的字符的位置,爲了求出窗口的大小,需要一個變量 left 來指向滑動窗口的左邊界
-
如果當前遍歷到的字符從未出現過,那麼直接擴大右邊界
-
如果之前出現過,那麼就分兩種情況,在或不在滑動窗口內
- 如果不在,那麼就沒事,當前字符可以加進來
- 如果在,需要先在滑動窗口內去掉這個已經出現過的字符了,去掉的方法並不需要將左邊界
left
一位一位向右遍歷查找,由於HashMap
已經保存了該重複字符最後出現的位置,所以直接移動left
指針就可以了。
-
維護一個結果
res
,每次用出現過的窗口大小來更新結果res
,就可以得到最終結果啦。
注意將 left
初始化爲 -1,在 i - left
時,不必判斷單個字符的情況了。
參見代碼如下:
// 執行用時 :40 ms, 在所有 C++ 提交中擊敗了28.37%的用戶
// 內存消耗 :11.1 MB, 在所有 C++ 提交中擊敗了100.00%的用戶
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if (s.size() == 0) return 0;
int res = 0, left = -1;
unordered_map<int, int> m;
for (int i = 0; i < s.size(); ++i) {
if (m.count(s[i]) and m[s[i]] > left) left = m[s[i]];
m[s[i]] = i;
res = max(res, i - left);
}
return res;
}
};
方法二:滑動窗口+vector映射數組+常規解法
下面這種寫法是上面解法的精簡模式,這裏可以建立一個 256 位大小的整型數組來代替 HashMap
,這樣做的原因是 ASCII
表共能表示 256 個字符,也是常見的哈希映射初始化方式,但是由於鍵盤只能表示 128 個字符,所以用 128 也行,然後全部初始化爲 -1,這樣的好處是: 不用像之前的 HashMap
一樣要查找當前字符是否存在映射對了,對於每一個遍歷到的字符,直接用其在數組中的值來更新 left
,因爲默認是 -1,而 left
初始化也是 -1,所以並不會產生錯誤,這樣就省了 if 判斷的步驟,其餘思路都一樣。
參見代碼如下:
// 執行用時 :0 ms, 在所有 C++ 提交中擊敗了100.00%的用戶
// 內存消耗 :10.1 MB, 在所有 C++ 提交中擊敗了100.00%的用戶
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<int> m(128, -1);
int res = 0, left = -1;
for (int i = 0; i < s.size(); ++i) {
left = max(left, m[s[i]]);
m[s[i]] = i;
res = max(res, i - left);
}
return res;
}
};
方法三:滑動窗口+HashSet+常規解法
下面這種解法使用了 HashSet
,核心算法和上面的很類似,把出現過的字符都放入 HashSet
中,遇到 HashSet
中沒有的字符就加入 HashSet
中並更新結果 res
,如果遇到重複的,則從左邊開始刪字符,直到刪到重複的字符停止。感覺這個的 滑動窗口 更加動感,更加形象。
// 執行用時 :32 ms, 在所有 C++ 提交中擊敗了41.72%的用戶
// 內存消耗 :13.6 MB, 在所有 C++ 提交中擊敗了100.00%的用戶
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int res = 0, left = 0, i = 0, n = s.size();
unordered_set<char> t;
while (i < n) {
if (!t.count(s[i])) {
t.insert(s[i++]);
res = max(res, (int)t.size());
}
else {
t.erase(s[left++]);
}
}
return res;
}
};