【LeetCode】無重復字符串最長子串


題目描述

給定一個字符串,請你找出其中不含有重復字符的 最長子串 的長度。

示例 1:

輸入: "abcabcbb"
輸出: 3 
解釋: 因為無重復字符的最長子串是 "abc",所以其長度為 3。

示例 2:

輸入: "bbbbb"
輸出: 1
解釋: 因為無重復字符的最長子串是 "b",所以其長度為 1。

示例 3:

輸入: "pwwkew"
輸出: 3
解釋: 因為無重復字符的最長子串是 "wke",所以其長度為 3。
     請注意,你的答案必須是 子串 的長度,"pwke" 是一個子序列,不是子串。

題目解析

這道題的目標是找出最長子串,並且該子串必須不包含重復字符,而且這個子串必須是原字符串中連續的一部分(見示例3中的解釋說明)。

拿到題目時先不要心急想什么騷操作,我們先從最普通的操作開始把題目解出來,然后再來看如何優化。

接下來,我們畫圖分析一下,先隨便弄一個長相普通的字符串:frankissohandsome,我們要從中找出我們想要的子串,那少不了需要遍歷,我們設置兩個變量fromto,分別存儲尋找的目標子串在原字符串中的首尾位置。

首先,fromto的初始值都為0(String的序號從0開始),子串長度length = 1,最大子串長度maxLength = 1

然后,我們將to的指向往后移動,並判斷新遍歷的字符是否已經存在於子串中,如果不存在,則將其加入子串中,並將length進行自增。

直到找到一個已存在於子串中的字符,或者to到達字符串的末尾。這里,我們找到了一個重復的s,序號為7,此時的子串為frankis,將此時的子串長度與最大子串長度相比較(目前為0),如果比最大子串長度大,則將最大子串長度設置為當前子串長度7

接下來,我們繼續尋找符合條件的子串,這里比較關鍵的一點是下一個子串的起始位置,這里我們將from直接跳到了序號為7的位置,因為包含ss的子串顯然都不能滿足要求。

然后我們依照之前的方法,找到第二個候選的子串sohand,長度為6,比目前的最大子串長度小,所以不是目標子串。

接着繼續尋找,找到另一個候選子串ohands,長度小於最大子串長度,不是我們的目標子串。

繼續尋找。

to到達了字符串末尾,找到另一個候選子串handsome,長度大於最大子串長度,這就是我們的目標子串。

於是我們的最大子串長度就輕松加愉快的找到了。接下來的事情就是把上面的思路轉化成代碼。

這里只需要注意一下from的跳轉即可,每次跳轉的序號為to指向的字符在子串中出現的位置 + 1。

常規解法

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) return 0;
        int from = 0, to = 1, length = 1, maxLength = 1;
        // to遍歷直到字符串末尾
        while (to < s.length()){
            int site = s.substring(from, to).indexOf(s.charAt(to));
            if (site != -1){
                // to指向的字符已存在
                length = to - from;
                if (length > maxLength) maxLength = length;
                // from 跳轉到site+1的位置
                from = from + site + 1;
            }
            to++;
        }
        // 處理最后一個子串
        if (to - from > maxLength) {
            maxLength = to - from;
        }
        return maxLength;
    }
}

這里沒有什么騷操作,考慮好邊界情況就行了。有一個小細節需要注意,site代表的是子串中字符出現的位置,不是原字符串中的位置,因此from在跳轉時,需要加上自身原來的序號。還有最后一個子串的處理不要忘記,因為當to遍歷到字符串末尾時,會結束循環,最后一個子串將不會在循環內處理。

讓我們提交一下:

擊敗了73%的用戶,還不錯。

常規解法優化

想想看,還有沒有優化的空間呢?

那肯定是有的,首先我們想一想,當我們找到的最大子串長度已經比from所在位置到字符串末尾的位置還要長了,那就沒有必要再繼續下去了。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) return 0;
        int from = 0, to = 1, length = 1, maxLength = 0;
        // to遍歷直到字符串末尾
        while (to < s.length()){
            int site = s.substring(from, to).indexOf(s.charAt(to));
            if (site != -1){
                // to指向的字符已存在
                length = to - from;
                if (length > maxLength) {
                    maxLength = length;
                }
                // 判斷是否需要繼續遍歷
                if (maxLength > s.length() - from + 1) return maxLength;
                from = from + site + 1;
            }
            to++;
        }
        // 處理最后一個子串
        if (to - from > maxLength) {
            maxLength = to - from;
        }
        return maxLength;
    }
}

另外要處理類似bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb這樣的字符串,上面的方法還是有很大優化空間的,我們可以用一個HashSet來存儲所有元素,利用其特性進行去重,如果找到的子串長度已經等於HashSet中的元素個數了,那就不用再繼續查找了。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) return 0;
        int from = 0, to = 1, length = 1, maxLength = 0;
        Set<Character> set = new HashSet<>();
        for (int i = 0; i < s.length(); i++){
            set.add(s.charAt(i));
        }
        // to遍歷直到字符串末尾
        while (to < s.length()){
            int site = s.substring(from, to).indexOf(s.charAt(to));
            if (site != -1){
                // to指向的字符已存在
                length = to - from;
                if (length > maxLength) {
                    maxLength = length;
                }
                if (maxLength > s.length() - from + 1) return maxLength;
                if (maxLength >= set.size()) return maxLength;
                from = from + site + 1;
            }
            to++;
        }
        // 處理最后一個子串
        if (to - from > maxLength) {
            maxLength = to - from;
        }
        return maxLength;
    }
}

再提交一下:

哈哈哈哈,翻車了,所以這里引入一個HashSet用空間來換時間的方式不一定合適,看來測試用例里像bbbbbbbbbbbbbb這樣的用例並不多啊。

那么今天的翻車就到此為止了,如果覺得對你有幫助的話記得點個關注哦。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM