动态规划算法OJ刷题(3)

这篇具有很好参考价值的文章主要介绍了动态规划算法OJ刷题(3)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

CC19 分割回文串-ii

问题描述

给出一个字符串s,分割s使得分割出的每一个子串都是回文串。计算将字符串s分割成回文串的最小切割数。例如:给定字符串s=“aab”,返回1,因为回文分割结果[“aa”,“b”]是切割一次生成的。

解题思路

方法1:用一维数组来完成,O(N^3)

注意转移方程必须是让两个相邻状态之间一步完成。

  • 状态方程F(i) : 到第i个字符所需要的最小分割次数
  • 状态转移方程 : j<i && str[j+1, i]是回文串,若F(i)=0【表示从第1个字符到第i个字符是回文串】F(i, j) = min(F(j) + 1)
  • 初始条件 : F(i) = i - 1 ==> F(1) = 0。即单个字符只需要切0次,因为单子符都为回文串,2个字符最大需要1次,3个2次…【因为状态转移方程中要取min,那么F(i)要给一个最大的分割次数i-1】
  • 返回结果 : F(s.size())

此题i = 0是无效状态。

动态规划算法OJ刷题(3),算法刷题,算法,动态规划,c++

//时间复杂度O(N^3)
class Solution {
  public:
    /**
     *
     * @param s string字符串
     * @return int整型
     */

    bool isPal(string& str, int start, int end) {
        while (start < end) {
            if (str[start] != str[end]) {
                return false;
            }
            start++;
            end--;
        }
        return true;
    }

    int minCut(string s) {
        // write code here
        int len = s.size();
        if (0 == len) return 0;
        
        //先判断整体是不是回文串
        if (isPal(s, 0, len-1)) return 0;

        vector<int> ret(len + 1);
        //初始条件
        for (int i = 1; i <= len; ++i) {
            ret[i] = i - 1;
        }
        //状态转移方程
        for (int i = 2; i <= len; ++i) {
            //先判断整体是不是回文串
            if (isPal(s, 0, i - 1)) {
                ret[i] = 0;
                continue;
            }
            //j<i && str[j, i-1]是回文串
            for (int j = 1; j < i; ++j) {
                if (isPal(s, j, i - 1)) {
                    ret[i] = min(ret[i], ret[j] + 1);
                }
            }
        }
        return ret[len];
    }
};

方法2:用空间换时间,用二维矩阵来保存是否为回文串的状态。时间复杂度O(N^2)

  • 状态方程F(i,j) : 区间[i, j]是否为回文串

  • 状态转移方程 : F(i, j) : s[i]==s[j] && F(i+1, j-1) 【如果字符串的首尾相同,就缩小范围再判断】

    需特殊处理:1个字符 i==j,F(i,j): true; 两个字符 i+1=j, F(i,j): s[i] == s[j]

  • 初始条件 : i==j,F(i,j): true

  • 返回结果 : F(s.size())

class Solution {
  public:
    /**
     *
     * @param s string字符串
     * @return int整型
     */

    bool isPal(string& str, int start, int end) {
        while (start < end) {
            if (str[start] != str[end]) {
                return false;
            }
            start++;
            end--;
        }
        return true;
    }

    vector<vector<bool>> getMat(string& s) {
        int n = s.size();
        vector<vector<bool>> Mat(n, vector<bool>(n, false));
        for (int i = n - 1; i >= 0; --i) {
            for (int j = i; j < n; j++) {
                if (j == i) Mat[i][j] = true;
                else if (j == i + 1) Mat[i][j] = (s[i] == s[j]);
                else Mat[i][j] = (s[i] == s[j] && Mat[i + 1][j - 1]);
            }
        }
        return Mat;
    }

    int minCut(string s) {
        int len = s.size();
        if (0 == len) return 0;
        //先判断整体是不是回文串
        if (isPal(s, 0, len - 1)) return 0;

        vector<vector<bool>> Mat = getMat(s);
        vector<int> ret(len + 1);
        for (int i = 0; i <= len; ++i) {
            ret[i] = i - 1;
        }

        for (int i = 2; i <= len; ++i) {
            for (int j = 0; j < i; ++j) {
                if (Mat[j][i - 1])
                    ret[i] = min(ret[i], ret[j] + 1);
            }
        }
        return ret[len];
    }
}

CC77 编辑距离

题目描述

给定两个单词word1和word2,请计算将word1转换为word2至少需要多少步操作。你可以对一个单词执行以下3种操作:a)在单词中插入一个字符;b)删除单词中的一个字符;c)替换单词中的一个字符。

就是指从1个字符串转成另一个字符串所需的最少编辑操作次数

解题思路

子问题:从word1的第i个字符到word2的第j个字符需要的操作距离

通过状态方程,可知

1、F(i, j-1)–>F(i,j):word1[1,i]、word2[1,j-1]==>word1[1,i]==word2[1,j],字符1的前i个字符和字符2的前j-1个字符,通过对字符2进行1次插入第j个字符操作,使得字符1的前i个字符和字符2前j个字符相等;

2、F(i-1, j) -->F(i,j):word1[1,i-1]、word2[1,j]==>word1[1,i]==word2[1,j],字符1的前i个字符和字符2的前j-1个字符,通过对字符1进行1次删除第i个字符的操作,使得字符1的前i个字符和字符2前j个字符相等;

3、F(i-1, j-1)–>F(i,j):当word1[i]!=word2[j]时,才需要此操作。word1[1,i-1]、word2[1,j-1]==>word1[1,i]==word2[1,j],通过对字符1的第i个字符进行1次替换操作,使得字符1的前i个字符和字符2前j个字符相等;

  • 状态方程F(i,j) : 字符1前i个字符到字符2前j个字符的编辑距离
  • 状态转移方程 : F(i, j) = min(F(i, j-1) + 1, F(i-1, j) + 1, F(i-1, j-1) + (word1[i]==word2[j]) ? 0 : 1))【1、F(i, j-1) + 1 = F(i, j); 2、F(i-1, j) + 1 = F(i, j); 3、F(i-1, j-1) + (word1[i]==word2[j]) ? 0 : 1) = F(i, j);】
  • 初始条件 : F(i,0)=j; F(0,j)=i;
  • 返回结果 : F[row] [col]
#include <vector>
class Solution {
  public:
    /**
     *
     * @param word1 string字符串
     * @param word2 string字符串
     * @return int整型
     */
    int minDistance(string word1, string word2) {
        // write code here
        int row = word1.size();
        int col = word2.size();

        vector<vector<int>> minD(row + 1, vector<int>(col + 1));
        //初始化
        for (int i = 0; i <= col; ++i) minD[0][i] = i;
        for (int i = 1; i <= row; ++i) minD[i][0] = i;

        for (int i = 1; i <= row; ++i) {
            for (int j = 1; j <= col; ++j) {
                //删除 删除
                minD[i][j] = min(minD[i][j - 1], minD[i - 1][j]) + 1;
                //替换
                if (word1[i - 1] == word2[j - 1]) minD[i][j] = min(minD[i][j],
                            minD[i - 1][j - 1]);
                else minD[i][j] = min(minD[i][j], minD[i - 1][j - 1] + 1);
            }
        }
        return minD[row][col];
    }
};

动态规划算法OJ刷题(3),算法刷题,算法,动态规划,c++

CC36 不同的子序列

题目描述

给定两个字符串S和T,返回S子序列等于T的不同子序列个数有多少个?字符串的子序列是由原来的字符串删除一些字符(也可以不删除)在不改变相对位置的情况下的剩余字符。例如,"ACE"是"ABCDE"的子序列,但是"AEC"不是;S=“nowcccoder”, T = “nowccoder” , 返回3【now+ccoder/nowc+coder/nowcc+oder】;S=“rabbbit”, T = “rabbit” , 返回3【ra+bbit/rab+bit/rabb+it】;

解题思路

子问题:S的前i个字符构成的子串与T的前j个字符相同的子序列的个数

子串的长度要大于等于T的长度才能找到有相同的子串。

  • 状态方程F(i,j) : S的前i个字符已经与T的前j字符相同了

  • 状态转移方程 :

    在F(i,j)处需要考虑S[i] = T[j] 和 S[i] != T[j]两种情况:

    ​ 当S[i] = T[j]: 1、 让S[i]匹配T[j],则F(i,j) = F(i-1,j-1); 2、让S[i]不匹配T[j], 则问题就变为S[1:i-1]中的子串与T[1:j]相同的个数,则F(i,j) = F(i-1,j) , 故S[i] = T[j]时,F(i,j) = F(i-1,j-1) + F(i-1,j)

    ​ 当S[i] != T[j]: 问题退化为S[1:i-1]中的子串与T[1:j]相同的个数,故,S[i] != T[j]时,F(i,j) = F(i-1,j)

  • 初始条件 : F(i,0) = 1 —> S的子串与空串相同的个数,只有空串与空串相同

  • 返回结果 : F(S.size(), T.size())文章来源地址https://www.toymoban.com/news/detail-619590.html

class Solution {
  public:
    /**
     *
     * @param S string字符串
     * @param T string字符串
     * @return int整型
     */
    int numDistinct(string S, string T) {
        // write code here
        int s_size = S.size();
        int t_size = T.size();
        // S的长度小于T长度,不可能含有与T相同的子串
        if (S.size() < T.size()) return 0;
        // T为空串,只有空串与空串相同,S至少有一个子串,它为空串
        if (T.empty()) return 1;
        // F(i,j),初始化所有的值为0
        vector<vector<int> > f(s_size + 1, vector<int>(t_size + 1, 0));
        // 空串与空串相同的个数为1
        f[0][0] = 1;
        for (int i = 1; i <= s_size; ++i) {
            // F(i,0)初始化
            f[i][0] = 1;
            for (int j = 1; j <= t_size; ++j) {
                // S的第i个字符与T的第j个字符相同
                if (S[i - 1] == T[j - 1]) {
                    f[i][j] = f[i - 1][j] + f[i - 1][j - 1];
                } else {
                    // S的第i个字符与T的第j个字符不相同
                    // 从S的前i-1个字符中找子串,使子串与T的前j个字符相同
                    f[i][j] = f[i - 1][j];
                }
            }
        }
        return f[s_size][t_size];
    }
};

到了这里,关于动态规划算法OJ刷题(3)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【leetcode刷题之路】剑指Offer(4)——分治+排序算法+动态规划

    8 分治算法 8.1 【递归】剑指 Offer 07 - 重建二叉树 https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/   前序遍历是根左右,中序遍历是左根右,这也就意味着前序遍历的第一个节点是整棵树的根节点,顺着这个节点找到它在中序遍历中的位置,即为in_root,那么in_root左边的都在左子

    2024年02月11日
    浏览(39)
  • 【算法深度探索】动态规划之旅(1):挑战OJ题海,解锁15道经典难题,让你成为DP大师!

    📃 博客主页: 小镇敲码人 🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏 🌏 任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。🍎🍎🍎 ❤️ 什么?你问我答案,少年你看,下一

    2024年04月11日
    浏览(22)
  • 【leetcode刷题之路】初级算法(2)——链表+树+排序和搜索+动态规划

    3.1 【链表】删除链表中的节点 https://leetcode.cn/problems/delete-node-in-a-linked-list/ 给出的就是要删除的那个节点,直接前后移动就行了。 3.2 【双指针】删除链表的倒数第 N 个结点 https://leetcode.cn/problems/remove-nth-node-from-end-of-list/ 利用双指针left和right,首先让right遍历n个节点,再让两

    2024年02月10日
    浏览(34)
  • 力扣算法刷题Day39|动态规划:不同路径 I&II

    力扣题目:#62.不同路径 刷题时长:参考题解后10min 解题方法:动规 复杂度分析 时间O(m*n) 空间O(m*n) 问题总结 初始化二维数组的python语法:i 对应 m,j 对应n 二维遍历顺序,从上到下从左到右通过两层for循环实现,其中startindex应为1 本题收获 动规思路 确定dp数组及下标的含义

    2024年02月12日
    浏览(41)
  • 算法刷题Day 38 动态规划理论基础+斐波那契数+爬楼梯

    动态规划的解题步骤: 确定 dp 数组(dp table)以及下标的含义 确定递推公式 dp 数组如何初始化 确定遍历顺序 举例推导 dp 数组 很基础

    2024年02月15日
    浏览(24)
  • 【算法日志】动态规划刷题:01背包问题,多重背包问题(day37,day38)

    目录 前言 目标和(01背包) 一和零(01背包) 零钱兑换(多重背包) 排列总和(多重背包) 这两天都是背包问题,其中的01背包的一些应用问题需要一定的数学建模能力,需要i将实际问题简化成我们熟悉的背包问题;而这两天的多重背包问题还算比较基础,但也要我明白了

    2024年02月11日
    浏览(33)
  • 力扣算法刷题Day42|动态规划:01背包问题 分割等和子集

    力扣题目:01背包问题(二维数组) 刷题时长:参考题解 解题方法:动态规划 + 二维dp数组 复杂度分析 时间 空间 问题总结 理解递推公式困难 本题收获 动规思路:两层for循环,第一层i遍历物品,第二层j枚举背包容量以内所有值 确定dp数组及下标的含义:dp[i][j] 表示从下标

    2024年02月13日
    浏览(34)
  • 力扣算法刷题Day44|动态规划:完全背包问题 零钱兑换II 组合总和Ⅳ

    力扣题目:#518.零钱兑换II(完全背包组合问题) 刷题时长:7min 解题方法:动态规划(完全背包) 复杂度分析 时间复杂度: O(mn),其中 m 是amount,n 是 coins 的长度 空间复杂度: O(m) 问题总结 对递推公式的理解 本题收获 题意转换:纯完全背包是凑成背包最大价值是多少,而本

    2024年02月13日
    浏览(28)
  • 贪心算法OJ刷题(1)

    指所求问题的整体最优解可以通过一系列局部最优的选择来到达。是不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。 贪心算法的基本思路是从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考

    2023年04月25日
    浏览(24)
  • 动态规划——OJ题(一)

    📘北尘_ :个人主页 🌎个人专栏 :《Linux操作系统》《经典算法试题 》《C++》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 状态表⽰: 这道题可以「根据题⽬的要求」直接定义出状态表⽰: dp[i] 表⽰:第 i 个泰波那契数的值。 状态转移⽅程: 题⽬已经⾮常贴⼼的

    2024年02月04日
    浏览(21)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包