LeetCode题库整理【Java】—— 3 无重复字符的最长子串
LeetCode題庫整理【Java】
## 3 無重復(fù)字符的最長子串
題目:給定一個字符串,請你找出其中不含有重復(fù)字符的 最長子串 的長度。
示例 1:
輸入: “abcabcbb”
輸出: 3
解釋: 因?yàn)闊o重復(fù)字符的最長子串是 “abc”,所以其長度為 3。
示例 2:
輸入: “dvdf”
輸出: 1
解釋: 因?yàn)闊o重復(fù)字符的最長子串是 “vdf”,所以其長度為 3。
示例 3:
輸入: “pwwkew”
輸出: 3
解釋: 因?yàn)闊o重復(fù)字符的最長子串是 “wke”,所以其長度為 3。
請注意,你的答案必須是 子串 的長度,“pwke” 是一個子序列,不是子串。
方法:滑動窗口
我們先用一個例子來想一想如何在較優(yōu)的時間復(fù)雜度內(nèi)通過本題。
我們不妨以示例一中的字符串a(chǎn)bcabcbb 為例,找出 從每一個字符開始的,不包含重復(fù)字符的最長子串,那么其中最長的那個字符串即為答案。對于示例一中的字符串,我們列舉出這些結(jié)果,其中括號中表示選中的字符以及最長的字符串:
以(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)。
從中可以發(fā)現(xiàn),如果我們依次遞增地枚舉子串的起始位置,那么子串的結(jié)束位置也是遞增的!這里的原因在于,假設(shè)我們選擇字符串中的第 i 個字符作為起始位置,并且得到了不包含重復(fù)字符的最長子串的結(jié)束位置為 rp。那么當(dāng)我們選擇第 i+k 個字符作為起始位置時,首先從 i+k 到 rp 的字符顯然是不重復(fù)的,并且由于少了原本的第 k 個字符,我們可以嘗試?yán)^續(xù)增大 rp ,直到右側(cè)出現(xiàn)了重復(fù)字符為止。
我們使用兩個指針表示字符串中的某個子串的左右邊界。其中左指針代表著上文中**「枚舉子串的起始位置 i」**,而右指針即為上文中的 rp ;
在每一步的操作中,我們會將左指針向右移動一格,表示 我們開始枚舉下一個字符作為起始位置,然后我們可以不斷地向右移動右指針,但需要保證這兩個指針對應(yīng)的子串中沒有重復(fù)的字符。在移動結(jié)束后,這個子串就對應(yīng)著 以左指針開始的,不包含重復(fù)字符的最長子串。我們記錄下這個子串的長度;
在枚舉結(jié)束后,我們找到的最長的子串的長度即為答案。
在上面的流程中,我們還需要使用一種數(shù)據(jù)結(jié)構(gòu)來判斷是否有重復(fù)的字符,常用的數(shù)據(jù)結(jié)構(gòu)為哈希集合(即 C++ 中的 std::unordered_set,Java 中的 HashSet,Python 中的 set, JavaScript 中的 Set)。在左指針向右移動的時候,我們從哈希集合中移除一個字符,在右指針向右移動的時候,我們往哈希集合中添加一個字符。
LeetCode的官網(wǎng)中關(guān)于 滑動窗口 也有視頻及文字題解
以下代碼與官網(wǎng)版本略有差異,并通過了測試,僅供參考
class Solution {public int lengthOfLongestSubstring(String s) {//len記錄窗口滑動過程中存儲的當(dāng)前無重復(fù)字符的最長子串長度int len=0;//哈希集合,記錄每個字符是否出現(xiàn)過HashSet<Character> set = new HashSet<Character>();int n = s.length();//右指針,初始值為0,相當(dāng)于我們在字符串的左邊界,還未開始移動int rp = 0;for(int i=0; i<n; i++) { //i就相當(dāng)于左指針,從0開始if(i!=0) {set.remove(s.charAt(i-1));}while(rp<n && !set.contains(s.charAt(rp)) ) {set.add(s.charAt(rp));++rp;}len = Math.max(len, (rp-1)-i+1);}return len;} }測試結(jié)果:
在代碼調(diào)試過程中,尤其需要注意指針rp的位置,防止數(shù)組越界及使用哈希表判斷有無重復(fù)字符時出現(xiàn)異常錯誤
使用自己的Eclipse測試的完整代碼:
/*003 無重復(fù)字符的最長子串*/ package leet.code;import java.util.HashSet; import java.util.Scanner;public class LongestSubstring {public static void main(String[] args) {// TODO Auto-generated method stubLongestSubstring ls = new LongestSubstring();@SuppressWarnings("resource")Scanner sc = new Scanner(System.in);String str = sc.nextLine();int len = ls.lengthOfLongestSubstring(str);System.out.println(len);}public int lengthOfLongestSubstring(String s) {//len記錄窗口滑動過程中存儲的當(dāng)前無重復(fù)字符的最長子串長度int len=0;//哈希集合,記錄每個字符是否出現(xiàn)過HashSet<Character> set = new HashSet<Character>();int n = s.length();//右指針,初始值為-1,相當(dāng)于我們在字符串的左邊界,還未開始移動int rp = 0;for(int i=0; i<n; i++) { //i就相當(dāng)于左指針,從0開始if(i!=0) {//左指針右移時,要移除哈希表中的一個字符,在s中下標(biāo)為i-1set.remove(s.charAt(i-1));}while(rp<n && !set.contains(s.charAt(rp)) ) {//指針右移過程中,若字符不重復(fù),則往哈希表中添加一個字符set.add(s.charAt(rp));++rp;}len = Math.max(len, (rp-1)-i+1);//rp跳出while時向右移到了有重復(fù)字符的地方,計算Len時需要減1}return len;} }總結(jié)
以上是生活随笔為你收集整理的LeetCode题库整理【Java】—— 3 无重复字符的最长子串的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 非线性最优化(二)——高斯牛顿法和Lev
- 下一篇: java美元兑换,(Java实现) 美元