上篇文章分享的是暴力解決方法.暴力法很是簡單,可是它的速度不夠快!那麼咱們該如何去作優化了?算法
Given a string, find the length of the longest substring without repeating characters.swift
- Given "abcabcbb", the answer is "abc", which the length is 3.
- Given "bbbbb", the answer is "b", with the length of 1.
- Given "pwwkew", the answer is "wke", with the length of
- Note that the answer must be a substring, "pwke" is a subsequence and not a substring.
題目大意:給定一個字符串,找出不含有重複字符的最長子串的長度數組
解讀Examplebash
- 給定"abcabcbb",沒有重複字符的最長子串是"abc",那麼長度就是3
- 給定"bbbbb",最長子串就是"b",長度就是1
- 給定pwwkew,最長子串就是"wke",長度爲3,
- ==注意,==必須是一個子串."pwke",是子序列,而不是子串
使用暴力法解決是很是簡單,可是在暴力法中咱們會反覆檢查一個子字符串是否含有重複的字符.但其實沒有這個必要.數據結構
HashSet HashSet
是Java
中實現Set
接口.由哈希表支持.它不保證Set的迭代順序,可是它利用Hash的原理來確保元素的惟一性.在HashSet
中,元素都存到HashMap
鍵值對的key上面.而Value
時有一個統一的Hash
值.post
HashSet的插入 當有新的值加入時,底層的HashMap
會判斷Key值是否存在,若是不存在則插入新值.同時這個插入的細節會按照HashMap
插入細節.若是存在則不插入.優化
**滑動窗口:**是指的是數組/字符串問題的經常使用抽象概念.窗口一般在數組/字符串中由開始和結束的索引定義的一系列元素的集合.便可[i,j)(左閉,右開)
.而滑動窗口是能夠將2個邊界向某一個方向"滑動"的窗口.例如,咱們將[i,j)
向右滑動1個元素,則它將變成[i+1,j+1)(左閉,右開)
;ui
若是從索引i到j-1
之間的子字符串S[ij]
已經被檢查爲沒有重複字符.那則只須要檢查s[j]
對應的字符是否存在於子字符串s[ij]
;
因爲在C語言中是沒有集合這一個概念的.因此咱們使用java來實現.咱們能夠經過HashSet做爲活動窗口.那咱們只須要用O(1)的時間來完成對字符是否在當前子字符串的檢查.
咱們使用HashSet
將字符存儲在當前窗口[i,j),最初i=j
.而後咱們向右側滑動索引j
,若是它不在HashSet
中,則咱們會繼續滑動j
.直到s[j]
已經存在於HashSet
中,此時,咱們就已經找到的沒有重複字符的最長子串將會以索引i
開頭.若是咱們將全部的i
,都作如此操做便可獲得結果.
Java Code
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set<Character> set = new HashSet<>();
int ans = 0, i = 0, j = 0;
while (i < n && j < n) {
//試圖調整[i,j]的範圍
if (!set.contains(s.charAt(j))){
set.add(s.charAt(j++));
ans = Math.max(ans, j - i);
}
else {
set.remove(s.charAt(i++));
}
}
return ans;
}
}
複製代碼
o(2n) = o(n)
;在最糟糕的狀況下,每一個字符頂多被i,j訪問2次.o(min(m,n))
.窗口滑動法須要O(K)的空間,K指的是集合大小.而集合的大小取決於字符串n的大小以及字符串集的大小.