題目描述
給定一個字符串 s ,請你找出其中不含有重復字符的 最長子串 的長度。
示例 1:
輸入: s = "abcabcbb"
輸出: 3
解釋: 因為無重復字符的最長子串是 "abc",所以其長度為 3。
示例 2:
輸入: s = "bbbbb"
輸出: 1
解釋: 因為無重復字符的最長子串是 "b",所以其長度為 1。
示例 3:
輸入: s = "pwwkew"
輸出: 3
解釋: 因為無重復字符的最長子串是 "wke",所以其長度為 3。
請注意,你的答案必須是 子串 的長度,"pwke" 是一個子序列,不是子串。
題解
思路1: 暴力枚舉
i從0開始枚舉,枚舉以str[i]開頭的不重復字符串
以哈希表判重
// OC
+ (int)lengthOfLongestSubstring1:(NSString *)str {
int maxlen = 0;
for (int i=0; i<str.length; i++) {
NSMutableDictionary *hashDict = [[NSMutableDictionary alloc] init];
// 類似 str[i]
NSString *tempStr = [str substringWithRange:NSMakeRange(i, 1)];
hashDict[tempStr] = @1;
int right = i + 1;
while (right < str.length) {
NSString *tempRightStr = [str substringWithRange:NSMakeRange(right, 1)];
if (hashDict[tempRightStr] == nil) {
hashDict[tempRightStr] = @1;
}else{
break;
}
right ++ ;
}
maxlen = MAX(maxlen, hashDict.allKeys.count);
}
return maxlen;
}
// Swift
static public func lengthOfLongestSubstring1(_ s:String) -> Int {
var maxlen = 0
let sArr = Array(s)
let n = sArr.count
for i in 0..<n {
var hashDict = [String:Int]()
hashDict[String(sArr[i])] = 1
var right = i+1
while(right<n){
if hashDict[String(sArr[right])] == nil {
hashDict[String(sArr[right])] = 1
}else{
break
}
right += 1
}
maxlen = max(maxlen,hashDict.keys.count)
}
return maxlen
}
思路2: 滑動窗口
很明顯暴力枚舉是有很多無效操作的,我們可以對它進行優化
我們以abcabcbb為例
以(a)bcabcbb 開始的最長字符串為(abc)abcbb;
以a(b)cabcbb 開始的最長字符串為a(bca)bcbb;
以ab(c)abcbb 開始的最長字符串為ab(cab)cbb;
以abc(a)bcbb 開始的最長字符串為abc(abc)bb;
以abca(b)cbb 開始的最長字符串為abca(bc)bb;
以abcab(c)bb 開始的最長字符串為abcab(cb)b;
以abcabc(b)b 開始的最長字符串為abcabc(b)b;
以abcabcb(b) 開始的最長字符串為abcabcb(b)。
我們發現,如果我們依次遞增地枚舉子串的起始位置,那么子串的結束位置也是遞增的!假設我們選擇字符串中的第k個字符作為起始位置,并且得到不包含重復字符的最長子串的結束位置為rk。那么當我們選擇第k+1個字符作為起始位置時,首先從k+1到rk的字符顯然是不重復的,并且由于少了原本的第k 個字符,我們可以嘗試繼續增大rk ,直到右側出現了重復字符為止。
這樣一來,我們就可以使用「滑動窗口」來解決這個問題了:
- 我們使用兩個指針表示字符串中的某個子串(或窗口)的左右邊界,其中左指針代表著上文中「枚舉子串的起始位置」,而右指針即為上文中的rk;
- 在每一步的操作中,我們會將左指針向右移動一格,表示我們開始枚舉下一個字符作為起始位置,然后我們可以不斷地向右移動右指針,
但需要保證這兩個指針對應的子串中沒有重復的字符。在移動結束后,這個子串就對應著以左指針開始的,不包含重復字符的最長子串。我們記錄下這個子串的長度; - 在枚舉結束后,我們找到的最長的子串的長度即為答案。
// OC
+ (int)lengthOfLongestSubstring2:(NSString *)str {
int maxlen = 0;
int rk = 0;
// 哈希集合,記錄每個字符是否出現過
NSMutableArray *hashArr = [[NSMutableArray alloc] init];
for (int i=0; i<str.length; i++) {
if (hashArr.count > 0) {
// 左指針向右移動一格,移除一個字符
[hashArr removeObjectAtIndex:0];
}
while (rk < str.length && ![hashArr containsObject:[str substringWithRange:NSMakeRange(rk, 1)]]) {
// 不斷地移動右指針
[hashArr addObject:[str substringWithRange:NSMakeRange(rk, 1)]];
rk ++;
}
// 第 i 到 rk 個字符是一個極長的無重復字符子串
maxlen = MAX(maxlen, rk-i);
}
return maxlen;
}
// Swift
static public func lengthOfLongestSubstring2(_ s:String) -> Int {
var maxlen = 0
let sArr = Array(s)
let n = sArr.count
// 哈希集合,記錄每個字符是否出現過
var hashset = [String]()
// 右指針,初始值為 0
var rk = 0
for i in 0..<n {
if hashset.count > 0 {
hashset.remove(at: 0)
}
while(rk<n && !hashset.contains(String(sArr[rk]))){
hashset.append(String(sArr[rk]))
rk += 1
}
maxlen = max(maxlen,rk-i)
}
return maxlen
}
思路3: 雙指針
假設兩個指針left,right,分別表示不重復字串的開始位置和結束位置。left,right初始為0,初始化一個哈希表記錄不重復的字符,如果哈希表不包含s[right],則right指針右移(+1)且計算最大子串,如果哈希表包含s[right],則left指針不停的右移(+1)且哈希表移除對應的字符
// OC
+ (int)lengthOfLongestSubstring3:(NSString *)str {
int maxlen = 0;
int left = 0;
int right = 0;
// 哈希集合,記錄每個字符是否出現過
NSMutableArray *hashArr = [[NSMutableArray alloc] init];
while (left<str.length && right<str.length) {
NSString *tempStr = [str substringWithRange:NSMakeRange(right, 1)];
if ([hashArr containsObject:tempStr]) {
[hashArr removeObject:tempStr];
left ++;
}else{
[hashArr addObject:tempStr];
right ++;
maxlen = MAX(maxlen, hashArr.count);
}
}
return maxlen;
}
// Swift
static public func lengthOfLongestSubstring3(_ s:String) -> Int {
let sArr = Array(s)
var maxlen = 0
var left = 0
var right = 0
var tableDict = [String:Int]()
while (left < sArr.count && right < sArr.count){
if tableDict[String(sArr[right])] != nil {
tableDict.removeValue(forKey: String(sArr[left]))
left += 1
}else{
tableDict[String(sArr[right])] = 1
right += 1
maxlen = max(maxlen,tableDict.keys.count)
}
}
return maxlen
}
參考:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters