1. 程式人生 > >Leetcode - longest-substring-without-repeating-characters

Leetcode - longest-substring-without-repeating-characters

ssi 二分思想 分答 lse char 應該 ble with online

題目

<https://leetcode.com/problems/longest-substring-without-repeating-characters>

題意

給出一個字符串,求每個字符都不相同的最長子串長度。

現在題目要求我們求出這兩個數的和。

Example 1:

Input: "abcabcbb"
Output: 3 
Explanation: The answer is "abc", with the length of 3. 

思路

錯誤的思路

二分 + Hash

1、既然要找每個字符都不相同的最長子串長度,那麽便可以使用二分思想。首先取中值,假如當前中值是存在每個字符都不相同的子串的,說明應該搜索右邊的區間。否則,搜索左邊區間。這個二分答案的時間復雜度大概是O(log n)。

2、這裏還涉及如何判斷是否存在長度為K的不相同子串。我的想法是,拿出每一個子串,然後對每個子串使用Hash思想,對字符出現次數進行統計,若有出現2次的字符,那麽說明該子串不成立。若遇到一個全部只出現1次的,那麽就終止搜索。這一步的時間復雜度是O(n^2)

很可惜,一共900多個樣例,最後一個超時了!

正確的思路

滑動窗口

看了題解,才發現大佬的做法真牛P。他的時間復雜度是O(n)。

大概思路是:維護一個字符串不相同的區間,下面把它稱為一個窗口。這個窗口只包含互不相同的連續字符。

具體的做法是:設定該區間的左右區間分別是i,j。然後從左到右遍歷字符串,對於每一個新來的字符,先判斷該字符是否在區間內出現了。

(1)若出現了,說明區間+新字符形成的新字符串是存在重復字符的。那麽不應該將字符加入該區間(窗口不應該向右擴增),而且應該將區間的左邊界收縮(窗口左邊往右收縮),直到對於新字符來說,區間內不存在重復字符。(該區間要時刻保證,只包含互不相同的連續字符)

(2)若沒在區間內出現過,說明當前存在更長的不相同的子串。故將該字符加入區間(窗口向右擴增),並且更新最大值。

正確的代碼

該代碼是參考題解寫的。

class Solution {
public:

    int lengthOfLongestSubstring(string s) {
        int a[256];
        int n = s.length();
        memset(a, 0, sizeof(int)*256);
        int i = 0;//“不相同子串”的左區間
        int j = 0;//“不相同子串”的右區間
        int ans = 0;
        while(i<n && j<n){
            //如果新的字符沒有在“不相同子串”中出現,說明有更大的不相同子串。
            if(a[s[j]] == 0){
                a[s[j]] = 1;
                j++;
                ans = max(ans, j-i);
            }
            else{
                a[s[i]] = 0;
                i++;
            }
            
        }
        return ans;
        
    }
};

錯誤的代碼

該代碼是辣雞我寫的,有1個樣例超時了。

class Solution {//toulanboy
public:

    int lengthOfLongestSubstring(string s) {
 
        
        long long left = 0;
        int right = s.length();
        long long a[300];
        while(left < right){
            int mid = ceil((left + right)/2.0);

            bool isExist = false;

            //判斷是否存在長度為mid的互不相同的子串
            for(int i=0; i+mid-1<s.length();++i){
                if(isExist){
                    break;
                }
                memset(a, 0, sizeof(long long)*300);
                for(int j=0; j<mid; ++j){
                    a[s[i+j]]++;
                }
                int sum = 0;
                for(int j=0; j<300; ++j){
                    if(a[j] == 1)
                        sum += 1;
                }
                if(sum == mid)
                    isExist = true;


            }

            if(isExist){
                left = mid;
            
            }
            else{
                right = mid - 1;
            }
        }
        return left;
    }
};

運行結果

Runtime: 12 ms, faster than 99.84% of C++ online submissions for Longest Substring Without Repeating Characters.
Memory Usage: 9.2 MB, less than 99.68% of C++ online submissions for Longest Substring Without Repeating Characters.

Leetcode - longest-substring-without-repeating-characters