LeetCode 3. Longest Substring Without Repeating Characters【滑动窗口模板题+序列DP思想】⭐⭐⭐

文章目录

  • 题目描述
  • 知识点
  • 结果
  • 实现
    • 码前思考
    • 代码实现
    • 码后反思

题目描述

LeetCode 3. Longest Substring Without Repeating Characters【滑动窗口模板题+序列DP思想】⭐⭐⭐_第1张图片

知识点

两种解法:

  • 动态规划
  • 滑动窗口

结果

LeetCode 3. Longest Substring Without Repeating Characters【滑动窗口模板题+序列DP思想】⭐⭐⭐_第2张图片

实现

码前思考

  1. 题中提到了“最长”二字,然后又是“字符串”问题,让我很容易地就去想到了动态规划求解;
  2. 既然想从动态规划入手,那么就得想想怎么定义dp数组?

    因为做了很多这种动态规划的题目,我下意识地想到的就是最长无重复子串一定是以某个字符结尾的,那么我就直接定义dp数组的含义为s[i]结尾的最长无重复子串的长度。

  3. 上面只是设想dp数组是这个,那怎么证明这个dp数组能满足最优子结构和重叠子问题呢?

    我们可以考虑对于字符s[i],因为我们要求的是子串而不是子序列,所以s[i]s[i-1]是有很大关系的,我们可以很容易得知,dp[i]最好的结果就是dp[i-1]+1,最坏的就是里面s[i-dp[i-1]]~s[i-1]中存在了一个位置s[k]==s[i],这样s[i]结尾的最长无重复子串的长度就变成了i-k

  4. 综上,可以得出代码了。

代码实现

//典型地动态规划题,划分子问题
//看见这种带最长,最短的就要想到最优化问题,想到最优化问题要想到动态规划
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        //获取长度
        int size = s.size();

        //特殊情况,进行特判
        if(size == 0){
            return 0;
        }

        //初始化动态规划数组
        vector<int> dp(size);

        //初始化递推边界条件
        dp[0] = 1;
        
        int  ans = 1;

        for(int i=1;i<size;i++){
            //初始化为这个
            dp[i] = dp[i-1]+1;
            for(int j=i-1;j>=i-dp[i-1];j--){
                if(s[j] == s[i]){
                    dp[i] = i-j; 
                }
            }
            ans = max(ans,dp[i]);
        }

        return ans;
    }
};

码后反思

  1. 这道题目是典型的有关字符串动规的题目,状态数组dp的定义比较常规,但是状态转移方程就需要多思考了,我也是思考了很久才想到的。
  2. 注意,字符串问题并不一定就要使用动态规划,不要把自己的思维僵化了,比如这道题还可以使用滑动窗口
  3. 滑动窗口解题:
    其实这道题的滑动窗口思想和上面的动规思想接近。
    LeetCode 3. Longest Substring Without Repeating Characters【滑动窗口模板题+序列DP思想】⭐⭐⭐_第3张图片
    代码:
//使用滑动窗口进行解题
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int size = s.size();

        //进行特判
        if(size == 0){
            return 0;
        }
        int len = 1;

        int left = 0;
        int right = 0;

        unordered_map<char,int> window;
        
        while(right < s.size()){
            char c = s[right];
            right++;

            //如果包含了这个字符,那么需要左移
            if(window[c] == 1){
                while(s[left] != c){
                    window[s[left]] = 0;
                    left++;
                }
                left++;
            }else{
                window[c] = 1;
            }

            if(right - left > len){
                len = right - left;
            }

        }
        return len;
    }
};

其实这里的right就是一个dp数组,代表的是以当前right为底,能够得到的最长距离,只不过这里由于使用了map,从而使用空间复杂度换取了时间复杂度。所以会运行起来比动态规划解法要快很多!!!

你可能感兴趣的:(#,双指针,#,动态规划,动态规划,字符串,算法,leetcode)