wy的leetcode刷题记录_Day28_动态规划完结篇

wy的leetcode刷题记录_Day28_动态规划完结篇

时间:2022-10-30

目录

  • wy的leetcode刷题记录_Day28_动态规划完结篇
    • 784. 字母大小写全排列
      • 题目介绍
      • 思路
      • 代码
      • 收获
    • 647. 回文子串
      • 题目介绍
      • 思路
      • 代码
      • 收获
    • 516. 最长回文子序列
      • 题目介绍
      • 思路
      • 代码
      • 收获

784. 字母大小写全排列

今天的每日一题是:784. 字母大小写全排列

题目介绍

  1. 给定一个字符串 s ,通过将字符串 s 中的每个字母转变大小写,我们可以获得一个新的字符串。
  2. 返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。

示例 1:
输入:s = “a1b2”
输出:[“a1b2”, “a1B2”, “A1b2”, “A1B2”]

示例 2:
输入: s = “3z4”
输出: [“3z4”,“3Z4”]

思路

首先对于大小写的转换,我们可以通过位运算来操作因为每个字符的大小转换相差32,我们可以通过异或32来转换,而对于搜索的话我们可以采用BFS或者DFS,这里我们采用BFS来操作:我们遍历每一个字符c:

  • 如果c为一个数字,则队列中的所有序列的末尾都加上一个c,修改后的序列放入队列
  • 如果c是一个字母,则所有的序列末尾分别加上c的小写形式和大写形式后,再放入队列
  • 如果队列中的字符长度等与s的长度,则表示搜索已经完成了

代码

class Solution {
public:
    vector<string> letterCasePermutation(string s) {
        vector<string> ans;
        queue<string> qu;
        qu.emplace("");
        while (!qu.empty()) {
            string &curr = qu.front();
            if (curr.size() == s.size()) {
                ans.emplace_back(curr);
                qu.pop();
            } else {
                int pos = curr.size();
                if (isalpha(s[pos])) {
                    string next = curr;
                    next.push_back(s[pos] ^ 32);//增加转换后的末位数的字符
                    qu.emplace(next);//放入
                }
                curr.push_back(s[pos]);                
            }
        }
        return ans;
    }
};

收获

使用BFS搜索s,最重要的是遍历顺序,不明白的可以打印出来看每一轮的情况,这样才能理解。

647. 回文子串

647. 回文子串

题目介绍

  1. 给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。
  2. 回文字符串 是正着读和倒过来读一样的字符串。
  3. 子字符串 是字符串中的由连续字符组成的一个序列。
  4. 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:
输入:s = “abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”

示例 2:
输入:s = “aaa”
输出:6
解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”

思路

方法一:动态规划

  1. 确定dp数组的含义:布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的⼦串是否是回⽂⼦串,如果是dp[i][j]为
    true,否则为false。
  2. 确定dp数组的递推公式:首先我们通过嵌套循环来比较俩个字符是否相等,如果不相等的话那肯定不是回文字符串,如果相等的话情况还得细分,当ij时,也就是遍历到同一个字符的情况那肯定是回文字符串,如果i-j1那么就是这俩个字符相邻相等那么肯定也是回文字符串,而其他情况则取决于内层的字符串也就是dp[i+1][j-1]的情况,如果内层字符串是回文字符串那么他就是回文字符串,反之不是回文字符串
  3. 初始化:一开始所有的都初始化为false
  4. 遍历顺序:对于这道题遍历顺序是非常重要的,因为我们的dp[i][j]大概率通过dp[i+1][j-1]推出,所以我们肯定是从下到上,从左到右开始遍历,即从左下向右上递推。

方法二:双指针法
⾸先确定回⽂串,就是找中⼼然后想两边扩散看是不是对称的就可以了。在遍历中⼼点的时候,要注意中⼼点有两种情况。⼀个元素可以作为中⼼点,两个元素也可以作为中⼼点。

代码

class Solution {
public:
    int countSubstrings(string s) {
        int n=s.size();
        vector<vector<bool>> dp(n,vector<bool>(n,false));
        int result=0;
        
        for(int i=n-1;i>=0;i--)
        {
            for(int j=i;j<n;j++)
            {
                if(s[i]==s[j])
                {
                    if(j-i<=1)
                    {
                        dp[i][j]=true;
                        result++;
                    }
                    // else if(i-j==1)
                    //     dp[i][j]=true;
                    else if(dp[i+1][j-1])
                    {
                        result++;
                        dp[i][j]=true;
                    }
                }
            }
        }
        return result;
    }
};
class Solution {
public:
    int countSubstrings(string s) {
        int result = 0;
        for (int i = 0; i < s.size(); i++) {
            result += extend(s, i, i, s.size()); // 以i为中⼼
            result += extend(s, i, i + 1, s.size()); // 以i和i+1为中⼼
        }
             return result;
        }
    int extend(const string& s, int i, int j, int n) {
        int res = 0;
        while (i >= 0 && j < n && s[i] == s[j]) {
            i--;
            j++;
            res++;
            }
        return res;
    }
};

收获

这道题比较重要的就是递推的遍历顺序,对于遍历顺序我们首先需要列出相应的递推公式,然后根据公式手绘一个图看一下具体的遍历方法,首先必须得保证不能超限,其次保证不能死循环,最重要的是一定是由已知的知去推未知的值。

516. 最长回文子序列

516. 最长回文子序列

题目介绍

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

示例 1:
输入:s = “bbbab”
输出:4
解释:一个可能的最长回文子序列为 “bbbb” 。

示例 2:
输入:s = “cbbd”
输出:2
解释:一个可能的最长回文子序列为 “bb” 。

思路

  1. 确定dp数组的含义:dp[i][j]:字符串s在[i, j]范围内最⻓的回⽂⼦序列的⻓度为dp[i][j]。
  2. 确定dp数组的递推公式:如果s[i]与s[j]相同,那么dp[i][j] = dp[i + 1][j - 1] + 2;如果s[i]与s[j]不相同,,那么分别加⼊s[i]、s[j]看看哪⼀个可以组成最⻓的回⽂⼦序列。加⼊s[j]的回⽂⼦序列⻓度为dp[i + 1][j]。加⼊s[i]的回⽂⼦序列⻓度为dp[i][j - 1]。那么dp[i][j]⼀定是取最⼤的,即:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
  3. 初始化:
  4. vector> dp(s.size(), vector(s.size(), 0)); for (int i = 0; i < s.size(); i++) dp[i][i] = 1;

代码

class Solution {
    public:
        int longestPalindromeSubseq(string s) {
        vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
            for (int i = 0; i < s.size(); i++) dp[i][i] = 1;
                for (int i = s.size() - 1; i >= 0; i--) {
                    for (int j = i + 1; j < s.size(); j++) {
                        if (s[i] == s[j]) {
                            dp[i][j] = dp[i + 1][j - 1] + 2;
                        } 
                        else {
                            dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
                        }
                }
            }
        return dp[0][s.size() - 1];
    }
};

收获

动态规划是一个非常重要的知识点,关键就在于最基础的三部,以及多刷题!

你可能感兴趣的:(Leetcode刷题记录,leetcode,动态规划,算法)