算法通关村第19关【黄金】| 继续盘点高频动态规划dp问题

news2024/12/25 23:47:18

回文串专题

1.最长回文串

思路:

确定dp:dp[i][j]子串是否是回文串

确定递推公式:

例如:aa|cbc|aa dp[2][4] = dp[3][3] true

  • 如果s[i] == s[j] 那么 dp[i][j] = dp[i+1][j-1]
  • 否则dp[i][j] = false

确定初始化:dp[i][i] = true,一个字母都是回文

确定遍历顺序:子串从长度2开始一直到len长度,从小到大。i从小到大,不可以更换顺序

两个一组从头遍历到尾,三个一组从头遍历到尾,456....len个一组遍历到尾

class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        if (len < 2) {
            return s;
        }
        int maxLen = 1;
        int begin = 0;
        // dp[i][j] 表示 s[i..j] 是否是回文串
        boolean[][] dp = new boolean[len][len];
        // 初始化:所有长度为 1 的子串都是回文串
        for (int i = 0; i < len; i++) {
            dp[i][i] = true;
        }
        for(int l = 2;l<=len;l++){
            for(int i = 0;i<len;i++){
                int j = l + i - 1;
                if(j>=len){
                    break;
                }
                dp[i][j] = false;
                if(s.charAt(i) == s.charAt(j)){
                    if(j-i<3){
                        dp[i][j] = true;
                    }else{
                        dp[i][j] = dp[i+1][j-1];
                    }
                    if(dp[i][j]&&j-i+1>maxLen){
                        maxLen = j-i+1;
                        begin = i;
                    }
                }
            }
        }
        return s.substring(begin, begin + maxLen);
    }
}

2. 分割回文串II

思路:

确定dp:dp[i][j]子串是否是回文串(上一问方法),f[i]第i个字符最少的分割次数

确定递推公式:

  • 如果dp[0][i]为true说明当前整个字符串就是回文串不需要分割,那么 f[i] = 0
  • 否则dp[0][i]为false说明当前字符串需要进行分割,对当前字符串进行遍历,从当前字符串第二个字符开始(第一个字母自身就是回文),是回文子串就分割,f[i] = Math.min(f[i],f[j]+1)一直分割找到最小分割次数

确定初始化:f[i] = Integer.MAX_VALUE

确定遍历顺序:从前往后

class Solution {
    public int minCut(String s) {
        int len = s.length();
        boolean[][] dp = new boolean[len][len];
        for(int i = 0;i<len;i++){
            dp[i][i] = true;
        }
        //标记回文子串
        for(int l = 2;l<=len;l++){
            for(int i = 0;i<len;i++){
                int j = l + i - 1;
                if(j>=len){
                    break;
                }
                dp[i][j] = false;
                if(s.charAt(i) == s.charAt(j)){
                    if(j-i<3){
                        dp[i][j] = true;
                    }else{
                        dp[i][j] = dp[i+1][j-1];
                    }
                }
            }
        }
        //分割次数
        int[] f = new int[len];
        for(int i = 0;i<len;i++){
            f[i] = Integer.MAX_VALUE;
        }
        
        for(int j = 0;j<len;j++){
            if(dp[0][j]){
                f[j] = 0;
            }else{
                for(int i = 0;i<j;i++){
                    if(dp[i+1][j]){
                        f[j] = Math.min(f[j],f[i] + 1);
                    }
                }
            }
        }
        return f[len-1];
    }
}

双串典型问题

1.最长公共子序列

思路:

确定dp:dp[i][j] text1前i个字符和text2前j个字符最长公共子序列

确定递推公式:

  • 如果s[i] == s[j],dp[i][j] = dp[i-1][j-1]+1
  • 否则,dp[i][j] = Max(dp[i-1][j],dp[i-1][j-1],dp[i][j-1])

确定初始化:默认都是0

确定遍历顺序:从前往后

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int len1 = text1.length();
        int len2 = text2.length();
        int[][] dp =new int[len1+1][len2+1];
        char[] s1 = text1.toCharArray();
        char[] s2 = text2.toCharArray();
        for(int i = 0;i<len1;i++){
            for(int j = 0;j<len2;j++){
                if(s1[i] == s2[j]){
                    dp[i+1][j+1] = dp[i][j] + 1;
                }else{
                    dp[i+1][j+1] = Math.max(dp[i][j+1],dp[i+1][j]);
                }
            }
        }
        return dp[len1][len2];
    }
}

2.编辑距离

思路:

p[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]

相等不相等两种情况:

if (word1[i - 1] == word2[j - 1])

那么就不用编辑,dp[i][j] = dp[i - 1][j - 1];

if (word1[i - 1] != word2[j - 1])

  • 操作一:word1删除一个元素,那么就是以下标i - 2为结尾的word1 与 j-1为结尾的word2的最近编辑距离 再加上一个操作。即 dp[i][j] = dp[i - 1][j] + 1;
  • 操作二:word2删除一个元素,那么就是以下标i - 1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 再加上一个操作。即 dp[i][j] = dp[i][j - 1] + 1;
  • 操作三:替换元素,word1替换word1[i - 1],使其与word2[j - 1]相同。就是i-2和j-2的最近编辑距离 再加上一个操作。因为此时是替换不是增删就是操作左上角元素
class Solution {
    public int minDistance(String word1, String word2) {
        char[] s1 = word1.toCharArray();
        char[] s2 = word2.toCharArray();
        int len1 = s1.length;
        int len2 = s2.length;
        int[][] dp = new int[len1+1][len2+1];
        for(int i = 0;i<=len1;i++){
            dp[i][0] = i;
        }
        for(int i = 0;i<=len2;i++){
            dp[0][i] = i;
        }
        for(int i = 0;i<len1;i++){
            for(int j = 0;j<len2;j++){
                if(s1[i] == s2[j]){
                    dp[i+1][j+1] = dp[i][j];
                }else{
                    dp[i+1][j+1] = Math.min(dp[i][j],Math.min(dp[i][j+1],dp[i+1][j]))+1;
                }
            }
        }
        return dp[len1][len2];
    }
}

 3.正则表达式匹配

思路:

确定dp:dp[i][j] s前i个字符和p前j个字符是否能匹配

确定递推公式:

  • 如果s[i] == p[j],dp[i][j] = dp[i-1][j-1]
  • 如果p[j] == '.',dp[i][j] = dp[i-1][j-1]
  • 如果p[j] == '*'
    • 如果s[i] == p[j-1]||p[j-1] == '.' 匹配0个dp[i][j] = dp[i][j-2],匹配1个dp[i][j] = dp[i][j-1],匹配多个dp[i][j] = dp[i-1][j]
    • 否则,匹配0个dp[i][j] = dp[i][j-2]

确定初始化:dp[0][0]=true 空串匹配

确定遍历顺序:从前往后

class Solution {
    public boolean isMatch(String s, String p) {
        int lens = s.length();
        int lenp = p.length();
        char[] sc = s.toCharArray();
        char[] pc = p.toCharArray();
        boolean[][] dp = new boolean[lens+1][lenp+1];
        dp[0][0] = true;
        for(int i = 0;i<=lens;i++){
            for(int j = 1;j<=lenp;j++){
                //从1开始防止越界
                if(i>0&&(pc[j-1] == '.'||pc[j-1] == sc[i-1])){
                    dp[i][j] = dp[i-1][j-1];
                }else if(pc[j-1] == '*'){
                    if(i>0&&(pc[j-2] == sc[i-1]||pc[j-2] == '.')){
                        //匹配0/1、多个,需要加上匹配0个,a ..* .*丢弃
                        dp[i][j]=dp[i][j-1]|dp[i-1][j]|dp[i][j-2];
                    }else{
                        dp[i][j] = dp[i][j-2];
                    }
                }
            }
        }
        return dp[lens][lenp];
    }
}

乘积最大子数组

思路:

确定dp:

dp[i]第i个字符最大乘积dp[i]=max(dp[i-1]*nums[i],nums[i]),这样忽略了负负得正的情况

max[i]当前子数组对应的最大乘积

min[i]当前子数组对应的最小乘积

确定递推公式: 

三种情况取最大和最小

max[i] = Math.max(max[i-1]*nums[i],Math.max(min[i-1]*nums[i],nums[i]));

确定初始化:

  • max[0] = nums[0];
  • min[0] = nums[0];
  • int res = nums[0];

确定遍历顺序:从前往后

class Solution {
    public int maxProduct(int[] nums) {
        int[] max = new int[nums.length];
        int[] min = new int[nums.length];
        max[0] = nums[0];
        min[0] = nums[0];
        int res = nums[0];
        for(int i = 1;i<nums.length;i++){
            max[i] = Math.max(max[i-1]*nums[i],Math.max(min[i-1]*nums[i],nums[i]));
            min[i] = Math.min(max[i-1]*nums[i],Math.min(min[i-1]*nums[i],nums[i]));
            if(max[i]>res){
                res = max[i];
            }
        }
        return res;
    }
}

股票专题

1.买卖股票最佳时机

思路:prices[i] - min就是最大差价

class Solution {
    public int maxProfit(int[] prices) {
        int min = prices[0];
        int gap = 0;
        int res = 0;
        for(int i = 1;i<prices.length;i++){
            if(prices[i]<min){
                min = prices[i];
            }
            gap = Math.max(gap,prices[i]-min);
        }
        return gap;
    }
}

2.买卖股票的最佳时机II

思路:

确定dp:

dp[i][0]不持有当前股票所有资金

dp[i][1]持有当前股票所有资金

确定递推公式:

  • dp[i][0] = Math.max(dp[i-1][1] + prices[i],dp[i-1][0]); 前一天持有当天卖出/前一天也不持有
  • dp[i][1] = Math.max(dp[i-1][0]-prices[i] ,dp[i-1][1]); 前一天不持有当天买入/前一天也持有

确定初始化:dp[0][0]=0 dp[0][1] = -prices[0]当天买入

确定遍历顺序:从前往后

class Solution {
    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][2];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for(int i = 1;i<prices.length;i++){
            dp[i][0] = Math.max(dp[i-1][1] + prices[i],dp[i-1][0]);
            dp[i][1] = Math.max(dp[i-1][0]-prices[i] ,dp[i-1][1]);
        }
        return dp[prices.length-1][0];
    }
}

3.买卖股票最佳时机III

思路:

确定dp:

dp[i][0][1]不持有当前股票所有资金,并且处于或者已完成第一次交易

dp[i][1][2]持有当前股票所有资金,并且处于或者已经完成第二次交易

确定递推公式:

  • dp[i][0][1] = Math.max(dp[i-1][0][1],dp[i-1][1][1]+prices[i]);继续不持有/第一次卖出
  • dp[i][1][1] = Math.max(dp[i-1][1][1],-prices[i]);继续持有/买入
  • dp[i][0][2] = Math.max(dp[i-1][0][2],dp[i-1][1][2] + prices[i]);继续不持有/第二次卖出
  • dp[i][1][2] = Math.max(dp[i-1][0][1]-prices[i],dp[i-1][1][2]);第二次买入/继续持有

确定初始化:dp[0][1][1]=-prices[0] dp[0][1][2] = -prices[0]持有状态说明买入

确定遍历顺序:从前往后

class Solution {
    public int maxProfit(int[] prices) {
        int[][][] dp = new int[prices.length][2][3];
        dp[0][0][0] = 0;
        dp[0][1][1] = -prices[0];
        dp[0][1][2] = -prices[0];
        for(int i = 1;i<prices.length;i++){
            dp[i][0][1] = Math.max(dp[i-1][0][1],dp[i-1][1][1]+prices[i]);
            dp[i][1][1] = Math.max(dp[i-1][1][1],-prices[i]);
            dp[i][0][2] = Math.max(dp[i-1][0][2],dp[i-1][1][2] + prices[i]);
            dp[i][1][2] = Math.max(dp[i-1][0][1]-prices[i],dp[i-1][1][2]);
        }
        return Math.max(dp[prices.length-1][0][1],dp[prices.length-1][0][2]);
    }
}

打家劫舍

思路:

确定dp:dp[i]到当前房屋所能获得的最大金额

确定递推公式:dp[i] = Math.max(dp[i-1],dp[i-2] + nums[i]);偷当前房屋和不偷当前房屋

确定初始化:dp[0] = nums[0] dp[1] = max(nums[0],nums[1])

确定遍历顺序:从前往后

class Solution {
    public int rob(int[] nums) {
        if(nums.length == 1){
            return nums[0];
        }
        if(nums.length == 0){
            return 0;
        }
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        dp[1] =  Math.max(nums[0],nums[1]);
        for(int i = 2;i<nums.length;i++){
            dp[i] = Math.max(dp[i-1],dp[i-2] + nums[i]);
        }
        return dp[nums.length-1];
    }
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1136190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu ARMv8编译Qt源码以及QtCreator

最近需要在NVIDIA小盒子上面跑一个程序&#xff0c;一开始想着在Ubuntu x64下交叉编译一版&#xff0c;后来发现libqxcb.so 这个库在configure时就会一直报错&#xff0c;多方查找怀疑可能是由于硬件不支持在x64环境下编译AMR架构的xcb库。 所以最后在ARM下直接编译Qt源码了&am…

ONNX: export failure ❌ 0.0s: Unsupported ONNX opset version: 17

python export.py --weights runs/train/exp6/weights/best.pt --include onnx yolov5 训练好的pt文件导出onnx 导出失败 异常信息&#xff1a; Fusing layers… YOLOv5s summary: 157 layers, 7012822 parameters, 0 gradients, 15.8 GFLOPs PyTorch: starting from runs/tra…

vue中时间控件

//组件 <template><div class"home-time"><div class"tab"><el-radio-group v-model"radio" change"radioChange"><el-radio-button label"1">天</el-radio-button><el-radio-butto…

蕉下冲锋衣:过冬利器还是昙花一现?

“双11”前夕&#xff0c;靠防晒单品小黑伞起家的蕉下上新“过冬”产品冲锋衣。 此前&#xff0c;蕉下于2022年4月和10月分别向港交所递交招股书&#xff0c;拟冲刺“中国城市户外第一股”&#xff1b;截止目前&#xff0c;其IPO申请状态已转为“失效”。 「不二研究」据蕉下…

记一次 .Net+SqlSugar 查询超时的问题排查过程

环境和版本&#xff1a;.Net 6 SqlSuger 5.1.4.* &#xff0c;数据库是mysql 5.7 &#xff0c;数据量在2000多条左右 业务是一个非常简单的查询&#xff0c;代码如下&#xff1a; var list _dbClient.Queryable<tb_name>().ToList(); tb_name 下配置了一对多的关系…

Calibre软件学习

Mentor calibre 软件 版图物理验证主要包括&#xff1a; ①设计规则检查&#xff08; DRC &#xff09; ②电学规则检查&#xff08; ERC &#xff09; ③版图与电路图一致性检查 ( LVS) LVS 版图与电路图一致性检查 ( Layout Versus Schematic, LVS) 的目的在于检查人工…

echarts案例之日历

一、此案例基于Vue3ts&#xff0c;效果展示&#xff1a; 二、单个属性的值&#xff1a; 1、visualMap.pieces 根据值自定义每个小块的颜色 pieces: [ { min: 0, // 最小值 max: 20, // 最大值 label: 未统计, color: rgba(27, 61, 71,1), }, { min: 20, max: 50, label: 优, …

机器学习第一周

一、概述 机器学习大致会被划分为两类&#xff1a;监督学习&#xff0c;无监督学习 1.1 监督学习 监督学习其实就是&#xff0c;给计算机一些输入x和正确的输出y&#xff08;训练数据集&#xff09;&#xff0c;让他总结x->y的映射关系&#xff0c;从而给他其他的输入x&a…

linux套接字选项API

获取套接字的选项值(getsockopt) 【头文件】 #include <sys/types.h> #include <sys/socket.h> 【函数原型】 int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen); 【函数功能】 用于获取一个套接字的选项 【参数含义】 […

视图,触发器与存储过程

python操作MySQL SQL的由来: MySQL本身就是一款C/S架构&#xff0c;有服务端、有客户端&#xff0c;自身带了有客户端&#xff1a;mysql.exe python这门语言成为了MySQL的客户端(对于一个服务端来说&#xff0c;客户端可以有很多) 操作步骤&#xff1a; 1. 先链接MySQL …

Compose横向列表和网格列表

横向列表LazyRow LazyRow和LazyColumn使用类似。 /*** 横向列表LazyRow*/ Composable fun LazyRowTest() {val context LocalContext.currentval dataList arrayListOf<Int>()for (index in 1..50) {dataList.add(index)}LazyRow {items(dataList) { data ->Box(mo…

大数据可视化BI分析工具Apache Superset实现公网远程访问

大数据可视化BI分析工具Apache Superset实现公网远程访问 文章目录 大数据可视化BI分析工具Apache Superset实现公网远程访问前言1. 使用Docker部署Apache Superset1.1 第一步安装docker 、docker compose1.2 克隆superset代码到本地并使用docker compose启动 2. 安装cpolar内网…

CentOS 安装 tomcat 并设置 开机自启动

CentOS 安装 tomcat 并设置 开机自启动 下载jdk和tomcat curl https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz curl https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.15/bin/apache-tomcat-10.1.15.tar.gz解压jdk和tomcat并修改目录名称 tar -z…

Linux下利用Docker快速部署Kafka

1.摘要 在本文中,介绍了利用Docker安装Kafka的基础环境要求; 利用Docker安装zookeeper过程; 利用Docker安装Kafka过程;进入容器配置生产者和消费者过程; 演示生产者和消费者通讯; 故障排查方法。 2.基础环境准备 提前准备一台安装Linux系统的主机或虚拟机,我这里安装的是Ubu…

Photoshop使用笔记总目录

Photoshop基础学习之工具学习 一、【Photoshop界面认识】 二、【 Photoshop常用快捷键】 三、【色彩模式与颜色填充】 四、【选区】 五、【视图】 六、【常用工具组】 七、【套索工具组】 八、【快速选择工具组】 九、【裁剪工具组】 十、【图框工具组】 十一、【吸取…

二叉树中的topk问题(带图详解)

&#x1f5e1;CSDN主页&#xff1a;d1ff1cult.&#x1f5e1; &#x1f5e1;代码云仓库&#xff1a;d1ff1cult.&#x1f5e1; &#x1f5e1;文章栏目&#xff1a;数据结构专栏&#x1f5e1; TopK问题 在给定的n的数据中&#xff0c;求出这n个数据中最大的k个数字 TopK的代码&a…

C++通过指针获取类的私有成员

前言 C并没有类似java的反射机制&#xff0c;可以暴力获取类的私有成员。然而C因程序员自行管理内存&#xff0c;所以可以通过指针干任何事情。当然&#xff01;操作指针是非常危险的&#xff0c;谨慎操作&#xff01; // dome.cpp : 此文件包含 "main" 函数。程序执…

HackTheBox-Starting Point--Tier 0---Redeemer

文章目录 一 题目二 实验过程 一 题目 Tags Redis、Vulnerability Assessment、Databases、Reconnaissance、Anonymous/Guest Access译文&#xff1a;redis、漏洞评估、数据库、侦察、匿名/访客访问Connect To attack the target machine, you must be on the same network.…

微信智能对话初体验:5分钟搭建免费专属的智能对话机器人

写在前面 投入AI创业几个月了&#xff0c;AI小程序快要上线了&#xff0c;已经在备案审批中&#xff0c;想一起AI搞事情的可以联系我看是否有合作机会。昨天刷X时&#xff0c;看到有人说微信新上线了这个平台&#xff1a;https://chatbot.weixin.qq.com/&#xff0c;赶紧试试&…

VulnHub metasploitable-1

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…