LeetCode-3 無重複字符的最長子串
- 題目:3. 無重複字符的最長子串
- 難度:中等
- 分類:字符串
- 解決方案:雙指針、滑動窗口
<!-- more -->
LeetCode前幾道題都是經典題,今天我們學習第3題無重複字符的最長子串,這道題在秋招面試中遇見過,再次相遇,如此親切。下面我們看看這道題的題目描述。
題目描述
給定一個字符串,請你找出其中不含有重複字符的最長子串的長度。
示例1:
1 2 3 |
|
示例2:
1 2 3 |
|
示例3:
1 2 3 4 |
|
分析
首先我們看看這個題的示例3,該示例中提示我們這個題需要求的字符串的子串而不是子序列,我們具體來看看什麼是子串,什麼是子序列。
子串:字符串中任意個連續的字符組成的子序列稱爲該串的子串。注意子串強調字符的連續性。
子序列:字符串中去掉任意個元素後得到的結果即爲該字符串的子序列。注意子序列中字符出現的次序與原字符串中字符出現的次序要保持一致。
區分子串和子序列後,我們再回過頭來看看這個題。我們先動手畫一畫示例1的解題過程,如下圖所示:
從上圖我們可以觀察出,可以使用雙指針(left
指針和right
指針)來維護一個滑動窗口,這個窗口內的字符都是沒有重複的,遍歷一趟字符串後就可以得到最大的子串,因此時間複雜度爲O(n)
。現在想一個問題:right
指針指向的字符怎麼確定它在前面是否出現過,若出現過,它出現的位置在哪兒?對於這個問題,做過LeetCode-1 兩數之和這道題的小夥伴們應該知道,我們可以使用HashMap
記錄一個字符是否出現以及出現後的位置。對於重複多次出現的字符,我們是保留所有出現的位置還是隻記錄一個位置?觀察示例1分析過程可以知道,我們只需要保存一個最大位置即可。還有一個關鍵點,我們如何確定left
指針的位置?這一點非常重要,需要分情況討論。
- 當目前
right
指針指向的字符未出現過,left
指針不需要移動; - 當目前
right
指針指向的字符出現過,如果該字符在窗口中,即該字符出現在當前left
指針的右邊,則通過HashMap
獲取字符的位置並向右移動一位即爲更新後left
的位置;如果該字符在窗口外面,即在當前left
指針的左邊,則不需要移動left
的位置。
分析完後,再看看java
代碼的具體實現:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class
Solution {
public
int
lengthOfLongestSubstring(String s) {
int
res =
0
;
if
(s.length() ==
0
)
return
res;
// 創建HashMap,用來保存字符與位置之間的對應關係
HashMap<Character, Integer> hashMap =
new
HashMap<>();
// 初始化左指針和右指針,並遍歷字符串
for
(
int
left =
0
, right =
0
; right < s.length(); right++){
// 判斷右指針指向的字符是否出現過
if
(hashMap.containsKey(s.charAt(right))){
// 確定左指針的位置
left = Math.max(left, hashMap.get(s.charAt(right))+
1
);
}
// 對於第一次出現的字符,保存該字符的位置;對於多次出現的字符,更新該字符出現的位置
hashMap.put(s.charAt(right), right);
// 更新窗口的大小,保存最大的窗口大小
res = Math.max(res, right-left+
1
);
}
return
res;
}
}
整個算法流程的時間複雜度爲O(n)
,空間複雜度爲O(1)
。