无重复字符的最长子串

题目描述

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。


方法和思路解析

方法一(暴力法):

假设有一个函数可以判断所给定的字符串中是否含有重复字符。当没有重复字符时返回true,否则返回false。此时,上面的问题就就只需要遍历所有的字符子串,判断是否是无重复字符的字符串。在此过程记录下最长符合条件的子串长度即可。代码如下:

# solution for python3
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        max_len = 0
        for i in range(0, len(s)):
            for j in range(i+1, len(s)+1):
                if self.allUnique(i, j, s):
                    max_len = max(max_len, j - i) 
        
        return max_len

    @staticmethod
    def allUnique(start, end, s):
        lis = []
        for i in range(start, end):
            if s[i] in lis:
                return False
            lis.append(s[I])
        return True
// solution for c++
class Solution {
public:
    bool allUnique(int start, int end, string s){
        vector<char> lis;
        for(int i = start; i < end; i++){
            if(find(lis.begin(), lis.end(), s[i]) != lis.end()){
                return false;
            }
            lis.push_back(s[I]);
        }

        return true;
    }

    int lengthOfLongestSubstring(string s) {
        int str_len = int(s.size());
        int max_len = 0;
        for(int i = 0; i < str_len; i++){
            for(int j = i+1; j < str_len+1; j++){
                if (allUnique(i, j, s)) max_len = max(max_len, j - i);
            }
        }

        return max_len;
    }
};

复杂度分析:

时间复杂度是O(n^3)。判断是否有重复字符的函数allUnique函数在[start, end)区间内花费的时间是O(j-i)。对于给定的 i,总共的时间是遍历所有 j 的总和:

所以总时间是:

方法二(滑动窗口):

第二种方法是滑动窗口。所谓窗口是一个概念抽象。它是指一个区间(这里是个左闭右开区间)。方法一在执行过程中会多次检索不含有重复字符的子串。可以通过滑动窗口的思想来减少重复工作。left表示窗口的左节点,right表示窗口的右节点。left和right之间存放着不重复的字符子串。每次我们向右检索,当下一个字符并不存在于窗口中的子串时,窗口有边界向右滑1(即right:=right+1)。如果已经存在了,则将left右滑1(即left:=left+1),并重复上述过程。

// solution for c++
int lengthOfLongestSubstring(string s) {
    if(s.empty()) return 0;
    int maxlen=1;
    int start=0, start_temp=0;
    for(int end=1; end<s.size(); ++end){
        start=start_temp;
        // 下面循环检查新加入的元素是否存在于前面的非重复子串中
        while(start<end){
            if(s[start]==s[end]){
                // 遇到重复字符,窗口直接调整为子串中重复字符的后面位置
                start_temp=start+1;
                break;
            }
            start++;
        }
        maxlen=max(maxlen, end-start_temp+1);
    }
    return maxlen;
}

复杂度分析:
时间复杂度:为O(2n) = O(n)。(最差情况当字符串中每个字符都一样时为2n)。

方法三(滑动窗口改良):

和上述方法相似,只是多一步记录了重复字符位置,使得发现重复字符时不用一点一点滑动窗口的左边界。方法如下:
left和right之间存放着不重复的字符子串。每次我们向右检索,当下一个字符并不存在于窗口中的子串时,窗口有边界向右滑1(即right:=right+1)。如果已经存在了,则left换成子串中重复的那个字符之后。接着重复上述过程。

# solution for python
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if s:
            max_len = 1
            left = 0
            dic = {}
            for curr in range(len(s)):
                if s[curr] in dic:
                    left = max(dic[s[curr]] + 1, left)
                    
                dic[s[curr]] = curr
                max_len = max(max_len, curr - left + 1)

            return max_len
        else:
            return 0
// solution for c++
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if (s.empty()){
            return 0;
        }else{
            int str_len = int(s.size());
            int max_len = 1;
            int left = 0;
            unordered_map<char, int> hash;
            
            for(int i = 0; i < str_len; i++){
                if (hash.find(s[i]) != hash.end()){
                    left = max(left, hash[s[i]]+1);
                }
                hash[s[i]] = i;
                max_len = max(max_len, i - left + 1);
            }

            return max_len;
        }
    }
};

复杂度分析:
时间复杂度:为O(n)。


题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。