1. 程式人生 > >【LeetCode】無重復字符串最長子串

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

情況 什麽 hand 末尾 直接 初始 這就是我 個數 子串

題目描述

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

示例 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這樣的用例並不多啊。

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

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