LeetCode刷题记录3-无重复字符的最长子串

题目

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

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

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

示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
Hash法 时间O(n^2),空间O(n)

Hash法存储,例如dvdf字符串获取最长子串,步骤如下:

hash<char, int>   存储对应字符出现的index
循环遍历
1. i =0,查找d是否在hash,不存在则将d+index加入hash => hash<d, 0>
2. i =1,查找v是否在hash,不存在则将v+index加入hash => hash<d, 0><v, 1>
3. i =2,查找d是否在hash,存在将对应的<d, 0>的index取出,重新将hash更新数据index+1到i,此时hash<v, 1><d, 2>
4. i =3,查找f是否在hash,不存在则将f+index加入hash => hash<v, 1><d, 2><f, 3>
5. 输出hash长度

代码如下:

int lengthOfLongestSubstring(string s) {
     
    if (s.empty())
    {
     
        return 0;
    }
    unordered_map<char, int> maxStrMap;
    int maxLen = 0;
    for (int i = 0; i < s.size(); ++i)
    {
     
        //无重复
        if (maxStrMap.find(s[i]) == maxStrMap.end())
        {
     
            maxStrMap[s[i]] = i;
            if (maxLen < maxStrMap.size())
            {
     
                maxLen = maxStrMap.size();
            }
        }
        //有重复
        else
        {
     
            //此时相当于遇到了重复,去除这个重复,重复点的下一个开始继续搞
            //此时重复的元素的下标 留dvdf, i = 2时maxStrMap[s[i]]记录着0,也就是index=0
            int index = maxStrMap[s[i]];
            maxStrMap.clear();
            for (int j = index + 1; j <= i; ++j)
            {
     
                maxStrMap[s[j]] = j;
            }
        }
    }
    return maxLen;
}

不过LeetCode运行之后。。。
LeetCode刷题记录3-无重复字符的最长子串_第1张图片
LeetCode刷题记录3-无重复字符的最长子串_第2张图片
运行运行结果太惨了,所以换了个方法时间复杂度O(2n)的滑动窗口以及不需要而外空间的O(n^2)滑动窗口方式

滑动窗口法 时间O(2n),空间O(n)

然鹅下面的方法LeetCode过不了。。。我本地使用测试用例没有任何问题,但是就是过不了。。。
核心思想类似于双指针,快指针先走,遇到相同字符慢指针走即可。

int lengthOfLongestSubstring1(string s) {
     
    if (s.empty())
    {
     
        return 0;
    }
    int maxLen = 0;
    int len = s.size();
    unordered_map<char, int> uniCharMap;
    int i = 0, j = 0;
    while (i < len && j < len)
    {
     
        if (uniCharMap.find(s[j]) == uniCharMap.end())
        {
     
            uniCharMap[s[j]] = j++;
            if (maxLen < j - i)
            {
     
                maxLen = j - i;
            }
        }
        else
        {
     
            uniCharMap.erase(s[i++]);
        }
    }
    return maxLen;
}

LeetCode刷题记录3-无重复字符的最长子串_第3张图片

优化滑动窗口法 时间O(n),空间O(n)

滑动窗口法可能需要最多2n个步骤,事实上,它可以被进一步优化为仅需要 n 个步骤。我们可以定义字符到索引的映射,而不是使用集合来判断一个字符是否存在。 当我们找到重复的字符时,我们可以立即跳过该窗口。hash存储了当前key,如果重复了,可以移动的下一个下标的位置。
代码如下:

int lengthOfLongestSubstring3(string s) {
     
    if (s.empty())
    {
     
        return 0;
    }
    int maxLen = 0;
    int len = s.size();
    unordered_map<char, int> uniCharMap;
    int i = 0;
    for (int j = 0; j < len; ++j)
    {
     
        if (uniCharMap.find(s[j]) != uniCharMap.end())
        {
     
            i = std::max(uniCharMap[s[j]], i);
        }
        maxLen = std::max(maxLen, j - i + 1);
        uniCharMap[s[j]] = j + 1;   //记录当前key如果重复,可以跳到的下一个位置value
    }
    return maxLen;
}

但是LeetCode运行并不是很快,也可能是调用unordered_map会消耗一些时间,最后有一个时间复杂度为O(n^2),空间复杂度为O(1)的反而运行极快。
LeetCode刷题记录3-无重复字符的最长子串_第4张图片

复杂版滑动窗口,时间O(n^2),空间O(1)

不借助而外空间的滑动窗口版本。代码比较简单,见下面。但是运行效率很高!!!

int lengthOfLongestSubstring2(string s) {
     
    if (s.empty())
    {
     
        return 0;
    }
    int maxLen = 0;
    int len = s.size();
    int pre = 0;
    for (int end = 0; end < len; ++end)
    {
     
        for (int i = pre; i < end; ++i)
        {
     
            //发现重复
            if (s[end] == s[i])
            {
     
                pre = i + 1;  //往后移动
                break;
            }
        }
        if (end - pre + 1 > maxLen)
        {
     
            maxLen = end - pre + 1;
        }
    }

    return maxLen;
}

LeetCode刷题记录3-无重复字符的最长子串_第5张图片

你可能感兴趣的:(算法,leetcode,算法,LeetCode)