剑指offer(C++)-JZ48:最长不含重复字符的子字符串(算法-动态规划)

简介: 剑指offer(C++)-JZ48:最长不含重复字符的子字符串(算法-动态规划)

题目描述:

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

数据范围:

s.length≤40000 s.length≤40000

示例:

输入:

"abcabcbb"


返回值:

3


说明:

因为无重复字符的最长子串是"abc",所以其长度为 3。

解题思路:

本题是动态规划的经典题目。有两个解题思路。

思路一:滑动窗口

  1. 设计一个滑动窗口,窗口的右边界先行,用哈希表统计字符出现次数。
  2. 当出现重复字符时,左边界出发缩小窗口直到重复字符消失。
  3. 持续刷新最值就可以了。


思路二:动态规划


  1. 用哈希表存放字符上次出现的位置下标;用长度比字符串大1的vector,存放截止到第i个字符时,能继续维持的子串长度,比如v[0]=0,v[1]=1,v[2]可能为1可能为2。
  2. 执行遍历。用哈希表判断当前字符是否为重复字符,如果不是重复字符,那就在前面子串长度基础上加1;若出现了重复字符,则该字符与其重复字符的距离为i-m[s[i]],但如果两者之间有别的重复字符,则需要考虑此类情况,可以认为在其重复字符之后的子串中,该字符未出现过,则有v[i]+1;所以,比较v[i]+1和i-m[s[i]]谁小取谁,因为小的子串没断开,后续可以继续连接,而断开的子串虽然长度大,但不可以继续增加了。
  3. 持续更新字符最新下标以及子串长度最大值。

测试代码:

思路一:滑动窗口

class Solution {
public:
    // 最长子串
    int lengthOfLongestSubstring(string s) {
        // 定义哈希表
        unordered_map<char, int> m;
        // 滑动窗口遍历
        int result = 0;
        for(int left = 0, right = 0; right < s.length(); ++right){
            // 窗口右边界先行,统计字符出现次数
            m[s[right]]++;
            // 当出现重复字符,窗口左边界右移缩小窗口直到重复字符消失
            while(m[s[right]] > 1){
                m[s[left]]--;
                left++;
            }
            // 持续刷新子串最大长度
            result = max(result, right - left + 1);
        }
        return result;
    }
};

思路二:动态规划

class Solution {
public:
    // 最长子串
    int lengthOfLongestSubstring(string s) {
        // 定义哈希表,存放的是字符出现的位置下标
        unordered_map<char, int> m;
        int result = 0;
        // v[i]表示截止到i个字符时,能继续维持的子串长度
        // 所以v[0]=0,v[1]=1
        vector<int> v = vector<int>(s.length() + 1, 0);
        // i是字符串中字符下标
        for(int i = 0; i < s.length(); ++i){
            // 当哈希表中没发现重复字符,那就在前面最长子串长度基础上+1
            if(m.find(s[i]) == m.end())
                v[i + 1] = v[i] + 1;
            // 若出现了重复字符,该字符与其重复字符的距离为i-m[s[i]]
            // 但如果两者之间有别的重复字符,那要考虑这类情况
            // 可以认为在其重复字符之后的子串中,该字符未出现过,则有v[i]+1
            // 所以v[i]+1和i-m[s[i]]谁小,取谁,因为小的这个子串没断开
            else
                v[i + 1] = min(v[i] + 1, i - m[s[i]]);
            // 刷新该字符最新下标
            m[s[i]] = i;
            // 刷新最值
            result = max(result, v[i + 1]);
        }
        return result;
    }
};


相关文章
|
5天前
|
存储 编译器 C语言
C++字符串大小写之for语句
C++字符串大小写之for语句
10 0
|
6天前
|
存储 缓存 算法
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
|
6天前
|
存储 算法 程序员
C++从入门到精通:2.2.1标准库与STL容器算法深度解析
C++从入门到精通:2.2.1标准库与STL容器算法深度解析
|
7天前
|
C++
【代码片段】【C++】获取当前时间戳并生成固定格式字符串
【代码片段】【C++】获取当前时间戳并生成固定格式字符串
9 0
|
11天前
|
算法
代码随想录算法训练营第五十六天 | LeetCode 647. 回文子串、516. 最长回文子序列、动态规划总结
代码随想录算法训练营第五十六天 | LeetCode 647. 回文子串、516. 最长回文子序列、动态规划总结
31 1
|
存储 编译器 Linux
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”
|
14天前
|
安全 C++
石头剪子布(字符串解法 C++)
石头剪子布(字符串解法 C++)
17 0
|
18天前
|
算法
算法系列--动态规划--背包问题(5)--二维费用背包问题(上)
算法系列--动态规划--背包问题(5)--二维费用背包问题(上)
19 0
|
18天前
|
算法
算法系列--动态规划--背包问题(4)--完全背包拓展题目(上)
算法系列--动态规划--背包问题(4)--完全背包拓展题目(上)
20 0
|
18天前
|
算法
算法系列--动态规划--背包问题(3)--完全背包介绍(下)
算法系列--动态规划--背包问题(3)--完全背包介绍(下)
16 0