每天 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;
}