每日一道 LeetCode (46):無重複字元的最長子串
每天 3 分鐘,走上演算法的逆襲之路。
前文合集
程式碼倉庫
GitHub: https://github.com/meteor1993/LeetCode
Gitee: https://gitee.com/inwsy/LeetCode
題目:無重複字元的最長子串
題目來源:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
給定一個字串,請你找出其中不含有重複字元的 最長子串 的長度。
示例 1:
輸入: "abcabcbb" 輸出: 3 解釋: 因為無重複字元的最長子串是 "abc",所以其長度為 3。
示例 2:
輸入: "bbbbb"
輸出: 1
解釋: 因為無重複字元的最長子串是 "b",所以其長度為 1。
示例 3:
輸入: "pwwkew"
輸出: 3
解釋: 因為無重複字元的最長子串是 "wke",所以其長度為 3。
請注意,你的答案必須是 子串 的長度,"pwke" 是一個子序列,不是子串。
解題方案一:暴力方案
看到這道題的第一個反應是這題我做過!!
然後我往前一頓翻,結果嘛。。。
emmmmmmmmmmm。。。
沒找著。
既然找不著那就開始想,這題是要求一個最長無重複的子串,重點總共就兩點:
- 無重複
- 子串
最後是要求一個最長的長度,這其實就是把所有的不重複子串的長度窮舉出來,然後取最大就完事兒。
經過上面的分析,我們成功的把這道題轉化成了一個求不重複子串的問題。
思路是滑動視窗,首先子串肯定是有長度的,即使只有 1 也是有長度的。
這時,我們指定兩個指標,一個左一個右,左右指標之間的內容實際上就是我們的子串,長度就是我們的子串長度。
兩個指標位於初始位置時,開始移動右指標,然後每次移動以後判斷指向的元素是否和之前已有的元素有重複,如果沒有重複就一直向右移,直到有重複的為止,這就是我們的第一個子串,然後記錄這個子串的長度。
接下來左指標右移一位,然後再重複上面的過程,一直到左指標移動完整個字串,這時我們就遍歷完成了所有的不重複的子串。
判斷子串是否用重複可以通過資料結構來判斷,通常常用的有雜湊表。
接下來就是程式碼實現:
public int lengthOfLongestSubstring(String s) {
Set<Character> setChars = new HashSet<>();
int length = s.length();
// 定義右指標
int right = -1;
// 定義返回結果
int result = 0;
for (int i = 0; i < length; ++i) {
if (i != 0) {
// 左指標右移一次,刪掉前一個字元
setChars.remove(s.charAt(i - 1));
}
while (right + 1 < length && !setChars.contains(s.charAt(right + 1))) {
// 移動右指標,像 set 中新增字元
setChars.add(s.charAt(right + 1));
++right;
}
result = Math.max(result, right - i + 1);
}
return result;
}
解題方案二:優化後
上面這個方案有個缺陷,就是每次左指標只是單純的 + 1 ,實際上左指標可以直接移動到右指標 + 1 的位置,因為當前的子串已經有重複了,直接跳過就好了。
public int lengthOfLongestSubstring_1(String s) {
int length = s.length(), result = 0;
Map<Character, Integer> map = new HashMap<>();
for (int left = 0, right = 0; right < length; right++) {
// 如果含有右指標指向的元素,則移動左指標
if (map.containsKey(s.charAt(right))) {
left = Math.max(map.get(s.charAt(right)), left);
}
result = Math.max(result, right - left + 1);
map.put(s.charAt(right), right + 1);
}
return result;
}
解題方案三:極致優化
上面的方案還有沒有優化空間,當然有,我們對迴圈次數已經沒辦法優化了,那麼還能優化的就剩下了判斷當前字元是否存在。
比雜湊表定址還要快的可能有什麼?當然是直接運算元組咯~~~
首先可以定義一個 128 位的陣列,然後我們通過陣列進行判斷當前字元是否存在:
public int lengthOfLongestSubstring_2(String s) {
int n = s.length();
int result = 0;
int[] charIndex = new int[128];
for (int left = 0, right = 0; right < n; right++) {
char c = s.charAt(right);
left = Math.max(charIndex[c], left);
result = Math.max(result, right - left + 1);
charIndex[c] = right + 1;
}
return result;
}