DP刷题练习(五)

简介: 本文基于代码随想录学习,重点讲解动态规划(DP)在字符串问题中的应用。涵盖五道经典LeetCode题目:115. 不同的子序列、583. 两个字符串的删除操作、72. 编辑距离、647. 回文子串和516. 最长回文子序列。通过详细解析递推公式、初始化及代码实现,帮助读者深入理解DP解题思路,掌握状态转移方程的设计与优化方法。适合有一定基础、希望提升DP解题能力的学习者。

DP刷题练习(五)

文章内容学习自代码随想录,感谢carl!!!!

[TOC]

115. 不同的子序列 - 力扣(LeetCode)

居然连long long 也要爆,无奈unsigned long long

//dp定义:
unsigned long long dp[n+1][m+1];//以i-1作为结尾的字符串s中
                                 //有dp[i][j]个以j-1作为结尾字符串t
//递推公式:
//注意求的是s里面有多少个t
if(s[i-1]==t[j-1]) dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
//s[i-1]==t[j-1],那么如果你要使用这个s[i-1]==t[j-1],那你的数量就是继承之前的dp[i-1][j-1]
//但是你也可以不用这个s[i-1]去和t匹配,那就是dp[i-1][j]

else dp[i][j]=dp[i-1][j];
//没用s[i-1]可以用
//初始化:
for(int i=1;i<=n;i++) dp[i][0]=1;//子串是空字符串
for(int j=1;j<=m;j++) dp[0][j]=0;//母串是空字符串
dp[0][0]=1;//都是空字符串
int numDistinct(string s, string t) {
   
        int n=s.size();int m=t.size();
        unsigned long long dp[n+1][m+1];//以i-1作为结尾的字符串s中
                                        //有dp[i][j]个以j-1作为结尾字符串t
        for(int i=1;i<=n;i++) dp[i][0]=1;
        for(int j=1;j<=m;j++) dp[0][j]=0;
        dp[0][0]=1;
        for(int i=1;i<=n;i++)
        {
   
            for(int j=1;j<=m;j++)
            {
   
                if(s[i-1]==t[j-1]) dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
                else dp[i][j]=dp[i-1][j];
            }
        }
        return dp[n][m];
    }

583. 两个字符串的删除操作 - 力扣(LeetCode)

找两个字符串最长的公共子序列,然后分别删掉那些无法让其变成相同的字串字符即可

int minDistance(string word1, string word2) {
   
        int n=word1.size();int m=word2.size();
        int dp[n+1][m+1];//表示以i-1结尾的字串,j-1结尾的字串二者最长的公共子序列是dp[i][j]
        int cnt=0;
        int ans=0;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
   
            for(int j=1;j<=m;j++){
   
                if(word1[i-1]==word2[j-1]) dp[i][j]=dp[i-1][j-1]+1;
                else dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
                cnt=max(cnt,dp[i][j]);
            }
        }
        ans=n+m-2*cnt;
        return ans;
    }

另解:

int minDistance(string word1, string word2) {
   
        int n=word1.size();int m=word2.size();
        int dp[n+1][m+1];
        /*使i-1为尾word1,以j-1为尾的word2,为相同字符串的最小
        操作次数为dp[i][j]
        */
        for(int i=1;i<=n;i++) dp[i][0]=i;//和空串比那就有把i删干净
        for(int j=1;j<=m;j++) dp[0][j]=j;//同理
        dp[0][0]=0;//两个空串不用删
        for(int i=1;i<=n;i++){
   
            for(int j=1;j<=m;j++){
   
                if(word1[i-1]==word2[j-1]) dp[i][j]=dp[i-1][j-1];//这两个相同不用管
                else dp[i][j]=min(dp[i-1][j]+1,min(dp[i][j-1]+1,dp[i-1][j-1]+2));
            }                  //删i-1加一次次数,删j-1加一次次数,两个都删加两次次数
        }
        return dp[n][m];
    }

72. 编辑距离 - 力扣(LeetCode)

int dp[n+1][m+1];
/*以i-1为结尾的word1转换成以j-1为结尾的word2使用的最少操作次数为dp[i][j]*/
int minDistance(string word1, string word2) {
   
        int n=word1.size();int m=word2.size();
        int dp[n+1][m+1];
        dp[0][0]=0;
        for(int i=1;i<=n;i++) dp[i][0]=i;
        for(int j=1;j<=m;j++) dp[0][j]=j;
        for(int i=1;i<=n;i++){
   
            for(int j=1;j<=m;j++){
   
                if(word1[i-1]==word2[j-1]) dp[i][j]=dp[i-1][j-1];//这个位置相同不做操作
                else dp[i][j]=min(dp[i-1][j-1]+1,min(dp[i-1][j]+1,dp[i][j-1]+1));
            }//这个位置不同,可以转化其中一个,也可以把这个位置删掉
        }
        return dp[n][m];
    }

647. 回文子串 - 力扣(LeetCode)

 int countSubstrings(string s) {
   
        int n=s.size();
        int ans=0;
        bool dp[n][n];//[i,j]的字串是否回文是dp[i][j];
        memset(dp,false,sizeof(dp));
        for(int i=n-1;i>=0;i--)
        {
   
            for(int j=i;j<=n-1;j++)
            {
   
                if(s[i]==s[j]){
   
                    if(j-i<=1) {
   dp[i][j]=true;}
                    else dp[i][j]=dp[i+1][j-1];
                }
                if(dp[i][j]) ans++;
            }
        }
        return ans;
    }

516. 最长回文子序列 - 力扣(LeetCode)

 int longestPalindromeSubseq(string s) {
   
        int n=s.size();
        int dp[n][n];//[i,j]间最长回文子序列的长度
        memset(dp,0,sizeof(dp));
        for(int i=0;i<=n-1;i++) dp[i][i]=1;
        for(int i=n-1;i>=0;i--){
   
            for(int j=i+1;j<=n-1;j++){
   
                if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1]+2;
                else dp[i][j]=max(dp[i][j-1],dp[i+1][j]);
            }
        }
        return dp[0][n-1];
    }
目录
相关文章
|
11月前
|
Java 数据库 网络架构
菜鸟之路Day36一一Web开发综合案例(部门管理)
本文详细记录了基于Spring Boot的Web开发综合案例——部门管理功能的实现过程。从环境搭建到功能开发,涵盖数据库表设计、Spring Boot项目创建、依赖引入、配置文件设置以及Mapper、Service、Controller的基础结构构建。文章重点讲解了查询、删除、新增和修改部门信息的业务逻辑实现,遵循RESTful规范设计接口,并通过统一响应结果类`Result`优化前后端交互体验。借助Spring的IoC容器管理与MyBatis的SQL映射,实现了高效的数据操作与业务处理,最终完成部门管理的全功能开发。
371 12
|
11月前
|
SQL 关系型数据库 MySQL
菜鸟之路Day30一一MySQL之DML&DQL
本文介绍了MySQL中DML(数据操作语言)和DQL(数据查询语言)的核心用法。DML主要包括插入(insert)、更新(update)和删除(delete)语句,通过具体示例演示了如何对表数据进行增删改操作。DQL则聚焦于数据查询,涵盖基本查询、条件查询、聚合函数、分组查询、排序查询和分页查询等内容。文章通过丰富的SQL语句实例,帮助读者掌握如何高效查询和操作数据库中的数据,适合初学者学习和实践。
369 12
|
11月前
|
SQL 存储 关系型数据库
菜鸟之路Day29一一MySQL之DDL
本文《菜鸟之路Day29——MySQL之DDL》由作者blue于2025年5月2日撰写,主要介绍了MySQL中的数据定义语言(DDL)。文章详细讲解了DDL在数据库和表操作中的应用,包括数据库的查询、创建、使用与删除,以及表的创建、修改与删除。同时,文章还深入探讨了字段约束(如主键、外键、非空等)、常见数据类型(数值、字符串、日期时间类型)及表结构的查询与调整方法。通过示例代码,读者可以更好地理解并实践MySQL中DDL的相关操作。
359 11
|
10月前
|
SQL 监控 Java
菜鸟之路Day40一一事物管理&AOP
本文主要介绍了事物管理和AOP(面向切面编程)的相关知识。在事物管理部分,详细讲解了SQL中的事物控制语句以及Spring框架中的事物注解@Transactional的使用方法和属性配置,结合删除部门及员工的实际案例说明了事物传播行为的应用场景。AOP部分首先概述了其概念和优势,如代码复用与关注点分离,并通过计算service层方法运行时间的实例展示了AOP的实现方式。接着深入探讨了AOP的核心概念,包括切面、连接点、切入点和通知类型,最后通过一个综合案例——操作日志记录到数据库中,演示了如何利用自定义注解和AOP技术完成统一功能的添加。
253 40
|
11月前
|
SQL Java 数据库连接
菜鸟之路Day34一一Mybatis-基础操作
本文介绍了MyBatis的基础操作,包括删除、插入、修改和查询功能的实现。通过`@Delete`、`@Insert`、`@Update`和`@Select`注解完成对应操作,支持主键自动生成与返回。同时探讨了`#{}`和`${}`的区别,前者用于预编译SQL提升安全性,后者直接拼接但存在SQL注入风险。文章还提供了根据ID查询及条件查询的示例,并介绍了实体类属性与数据库字段不一致时的解决方案,如使用驼峰命名规则或配置映射关系,确保数据封装准确。
323 32
|
11月前
|
SQL XML Java
菜鸟之路Day33一一Mybatis入门
本文是《菜鸟之路Day33——Mybatis入门》的教程,作者blue于2025年5月18日撰写。文章介绍了MyBatis作为一款优秀的持久层框架,如何简化JDBC开发。通过创建SpringBoot工程、数据库表`user`及实体类`User`,引入MyBatis依赖并配置数据库连接信息,使用注解方式编写SQL语句实现查询所有用户数据的功能。此外,还展示了如何通过Lombok优化实体类代码,减少冗余的getter/setter等方法,提高开发效率。最后通过单元测试验证功能的正确性。
387 19
|
10月前
|
XML SQL 前端开发
菜鸟之路Day37一一Web开发综合案例(员工管理)
本文介绍了基于Web开发的员工管理综合案例,涵盖分页查询、条件分页查询、删除员工和新增员工四大功能模块。通过前后端交互,前端传递参数(如页码、每页记录数、查询条件等),后端使用MyBatis与PageHelper插件处理数据查询与操作。代码结构清晰,包括Controller层接收请求、Service层业务逻辑处理以及Mapper层数据访问,并结合XML动态SQL实现灵活的条件查询。此外,新增与删除功能分别通过POST与DELETE请求完成,确保系统功能完整且高效。
309 7
|
10月前
|
存储 前端开发 Java
菜鸟之路Day38一一Web开发综合案例(三)
本文介绍了Web开发中的文件上传与员工信息修改的综合案例,涵盖前端到后端的完整流程。重点讲解了阿里云OSS的集成,包括Bucket创建、密钥获取及SDK使用,并通过Spring Boot实现文件上传功能。同时,详细描述了员工信息查询与修改的操作逻辑,涉及Controller、Service和Mapper层代码实现。最后探讨了配置文件的优化,对比@Value与@ConfigurationProperties注解,展示了如何通过实体类批量注入配置参数,提升代码可维护性与灵活性。
277 1
|
11月前
|
关系型数据库 MySQL 程序员
菜鸟之路day31一一MySQL之多表设计
本文由blue撰写于2025年5月9日,主要介绍了MySQL多表设计的三种关系:一对多、一对一和多对多。一对多通过在“多”的一方添加关联字段实现,如部门与员工的关系;一对一通常用于单表拆分,通过唯一外键关联,例如学生与学生证的关系;多对多则需创建中间表,包含两个外键分别关联两方主键,如学生与课程的关系。文中还提供了实际案例,包括分类表、菜品表、套餐表及它们之间的关联设计,详细展示了多表设计的应用场景与实现方法。
395 14
|
存储 网络协议 API
Cpp网络编程Winsock API
本文详细介绍了使用Winsock API进行C++网络编程的过程,通过具体实例实现了一个基于TCP协议的C/S架构通信demo。文章从服务端与客户端两方面展开,涵盖网络库初始化、套接字创建、绑定IP与端口、监听与连接、数据收发到关闭连接等关键步骤。重点解析了`WSAStartup`、`socket`、`bind`、`listen`、`accept`、`connect`、`send`和`recv`等函数的使用方法及注意事项,并对比了标准库与Winsock库在链接时的区别。适合初学者了解Winsock网络编程基础。
647 35
下一篇
开通oss服务