多维动态规划-面试高频!-最长公共子序列和最长公共子串、回文串-c++实现和详解

news2024/12/28 21:32:41

1143. 最长公共子序列

中等

给定两个字符串 text1text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace""abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。

示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。

提示:

  • 1 <= text1.length, text2.length <= 1000
  • text1text2 仅由小写英文字符组成。

解法

  • 定义 dp [ i ] [ j ] 表示 text1[0:i-1] 和 text2[0:j-1] 的最长公共子序列。 状态定义特别重要,dp[ i] [ j] 其中i和j都是开区间,所以dp[0] [0]=0只是表示两个空串的最长公共子序列,这种方式省去了所有的初始化
class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        // 状态定义特别重要
        // dp[i][j] i和j都是开区间
        // 如果上一个都相等,就给下一个赋值
        // 遍历只是表现形式
        int M = text1.size();
        int N = text2.size();
        // dp的初始化
        vector<vector<int>> dp(M + 2, vector<int>(N + 2, 0));
        for (int i = 1; i <= M; i++) {
            for (int j = 1; j <= N; j++) {
                if (text1[i - 1] == text2[j - 1])
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
            }
        }
        return dp[M][N];
    }
};

BM66 最长公共子串

题目链接-牛客

  • 这道题我面试碰到了,当时不会做!没想到力扣没有这道题!

描述

给定两个字符串str1和str2,输出两个字符串的最长公共子串

题目保证str1和str2的最长公共子串存在且唯一。

数据范围: 1≤∣𝑠𝑡𝑟1∣,∣𝑠𝑡𝑟2∣≤50001≤∣str1∣,∣str2∣≤5000
要求: 空间复杂度 𝑂(𝑛2)O(n2),时间复杂度 𝑂(𝑛2)O(n2)

示例1

输入:

"1AB2345CD","12345EF"

复制

返回值:

"2345"

复制

备注:

1≤∣𝑠𝑡𝑟1∣,∣𝑠𝑡𝑟2∣≤5 0001≤∣str1∣,∣str2∣≤5000

解法

  • 本题的区别是子序列和子串的定义不同,所以本题如果两个字符不同的话就应该直接把dp清零!

  • 我自己的代码如下:

    #include <vector>
    class Solution {
      public:
        /**
         * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
         *
         * longest common substring
         * @param str1 string字符串 the string
         * @param str2 string字符串 the string
         * @return string字符串
         */
        string LCS(string str1, string str2) {
            // write code here
            int M = str1.size();
            int N = str2.size();
            int maxx = 0;
            vector<vector<int>>dp(M + 2, vector<int>(N + 2, 0)); //初始化、
            string res;
            for (int i = 1; i <= M; i++) {
                for (int j = 1; j <= N; j++) {
                    if (str1[i - 1] == str2[j - 1]) {
                        dp[i][j] = dp[i - 1][j - 1] + 1;
                        if (dp[i][j] > maxx) {
                            maxx = dp[i][j];
                            int l = dp[i][j];
                            res = str1.substr(i - l, l);
                        }
                    } else dp[i][j] = 0;
                }
            }
            return res;
        }
    };
    

后来看到别人更好的代码:

#include <iostream>
#include <vector>
#include <string>
using namespace std;

string LCS(const string& str1, const string& str2) {
    int maxLength = 0; // 记录最长公共子串的长度
    int maxLastIndex = 0; // 记录最长公共子串最后一个字符在字符串str1中的位置

    // 创建二维动态规划表,大小为(str1.length() + 1) x (str2.length() + 1)
    vector<vector<int>> dp(str1.length() + 1, vector<int>(str2.length() + 1, 0));

    // 遍历str1和str2的每一个字符
    for (int i = 0; i < str1.length(); i++) {
        for (int j = 0; j < str2.length(); j++) {
            // 当字符相等时,更新dp表的值
            if (str1[i] == str2[j]) {
                dp[i + 1][j + 1] = dp[i][j] + 1;

                // 如果当前公共子串的长度大于之前记录的最大值,则更新最大长度和最后一个字符的位置
                if (dp[i + 1][j + 1] > maxLength) {
                    maxLength = dp[i + 1][j + 1];
                    maxLastIndex = i;
                }
            } else {
                // 当字符不相等时,将dp表中的值设为0,表示没有公共子串
                dp[i + 1][j + 1] = 0;
            }
        }
    }

    // 使用substr函数截取最长公共子串
    return str1.substr(maxLastIndex - maxLength + 1, maxLength);
}


这里补充一个点,求回文串、回文数等等的通法:双指针中心扩散

最长回文串

  • 我先贴一份我自己写的代码,我的初始想法是,把s做一个reverse,然后求s1和s的最长公共子串,就是回文串
class Solution {
public:
    string longestPalindrome(string s) {
        // 最长回文子串=reverse的最长公共子串!
        string s1 = s;
        reverse(s.begin(), s.end());
        int M = s.size();
        vector<vector<int>> dp(M + 1, vector<int>(M + 1, 0));
        int maxlen = 0;
        int end = 0;
        for (int i = 1; i <= M; i++) {
            for (int j = 1; j <= M; j++) {
                if (s[i - 1] == s1[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    if (dp[i][j] > maxlen) {
                        maxlen = max(maxlen, dp[i][j]); // 最大值
                        end = i - 1;
                    }
                } else {
                    dp[i][j] = 0;
                }
            }
        }
        return s.substr(end - maxlen + 1, maxlen);
    }
};
  • 样例都过了,卡在了后面的一个样例,说明这个方法是错的!
    在这里插入图片描述
  • 下面附正确的代码(已经写上完整注释):
class Solution {
public:
    // 辅助函数,用于计算以 s[l] 和 s[r] 为中心的最长回文子串的长度
    int help(string s, int l, int r) {
        int len = s.size();

        // 当 l 和 r 不超出范围,并且 s[l] == s[r] 时,向外扩展
        while (l >= 0 && r < len && s[l] == s[r]) {
            l--;  // 左边指针左移
            r++;  // 右边指针右移
        }
        // 返回回文子串的长度,注意此时 l 和 r 已经多走了一步
        return r - l - 1; // 回文串的长度为 r - l - 1
    }

    // 主函数,返回最长的回文子串
    string longestPalindrome(string s) {
        int len = s.size();  // 字符串长度
        int begin = 0;  // 记录最长回文子串的起始位置
        int maxlen = 0; // 记录最长回文子串的长度

        // 遍历字符串中的每个字符,以它们作为回文的中心
        for (int i = 0; i < len; i++) {
            // 以 i 为中心扩展,找奇数长度的回文子串
            int len1 = help(s, i, i);
            // 以 i 和 i+1 为中心扩展,找偶数长度的回文子串
            int len2 = help(s, i, i + 1);

            // 更新最长回文子串的长度和起始位置
            if (len1 > maxlen || len2 > maxlen) {
                if (len1 > len2) {
                    // 如果奇数长度的回文子串更长
                    maxlen = len1;  // 更新最大长度
                    begin = i - (len1 - 1) / 2; // 计算起始位置
                } else {
                    // 如果偶数长度的回文子串更长
                    maxlen = len2;  // 更新最大长度
                    begin = i - (len2 - 2) / 2; // 计算起始位置
                }
            }
        }
        // 返回从起始位置开始的最大回文子串
        return s.substr(begin, maxlen);
    }
};

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

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

相关文章

UE驻网失败问题(三)

这个问题是lab问题&#xff0c;现象如下&#xff1a; 期望UE注册在SA网络下&#xff0c;咋一看没有5G MIB/SIB1打印&#xff0c;好像是没搜到5G小区&#xff0c;而实际上并不是这样。 在查看搜网过程时会发现如下log打印&#xff1a; [I nr5g_rrc_acq.c 3544] RRC ACQ: Band 41…

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

01 引言 随着时间的发展&#xff0c;大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用&#xff0c;随着人们期望的不断增加&#xff0c;目标也发生了巨大的变化。在短短的几个月的时间里&#xff0c;人们对大模型的认识已经从对其zero-shot能力感到惊讶&#xff0c…

ElasticSearch-Ingest Pipeline Painless Script

Ingest Node & Pipeline & Processor Ingest NodePipeline & Processor内置的 Processors创建 pipeline使用 pipeline 更新数据借助 update_by_query 更新已存在的文档Ingest Node VS Logstash Painless Ingest Node & Pipeline & Processor 应用场景&…

坚持与等待的区别!看了当年高考状元如今的现状,我才明白所谓名校的真相——早读(逆天打工人爬取热门微信文章解读)

快 机会来了 引言Python 代码第一篇 洞见 看了当年高考状元如今的现状&#xff0c;我才明白所谓名校的真相第二篇 股市 之 空窗期结尾 &#xff08;不是 你改名 怎么改群名字&#xff01; 这下每个人都知道王妈妈单身了&#xff09; 引言 昨天忘记写了 真的很抱歉 说下借口哈…

Invicti-Professional-V24.8.1

前言 Invicti 专业 Web 应用程序安全扫描器 自动、极其准确且易于使用的 Web 应用程序安全扫描程序&#xff0c;可自动查找网站、Web 应用程序和 Web 服务中的安全漏洞。 Invicti Professional Edition 是一款商业 Web 应用程序安全扫描器。它旨在自动查找和修复 Web 应用程…

VScode:快捷键和技巧

格式化文档 搜索文件名

助贷CRM系统:为金融中介行业打造全新营销管理模式

助贷CRM&#xff08;客户关系管理&#xff09;系统是针对金融中介行业&#xff0c;特别是从事贷款助贷业务的机构设计的一套综合管理系统。该系统旨在通过数字化、智能化的手段&#xff0c;优化金融中介机构的营销、销售、客户管理及服务流程&#xff0c;提升运营效率&#xff…

构建高效在线拍卖系统:SpringBoot实践

MySQL数据库 数据库是系统开发过程中不可或缺的一部分。 在WEB应用方面&#xff0c;MySQL AB开发了一个具有很大优势的MySQL关系数据库管理系统。 MySQL可以将数据存储在不同的表中&#xff0c;这非常灵活&#xff0c;并且还可以提高系统在实际应用中的速度。 数据库访问最常用…

创客匠人8月总结|所有赋能都是服务,都是为了帮客户拿结果

“九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。”每一份伟大的成就都源自不懈的积累与坚定的步伐。 作为“知识变现整体解决方案服务商”&#xff0c;我们始终站在时代的前沿&#xff0c;致力于为每一位知识IP搭建桥梁&#xff0c;直通知识变现之…

软件工程造价师习题练习 24

1.关于功能点方法&#xff0c;以下描述不正确的是( ) A. 可用于项目范围管理 B. 可用于澄清需求 C. 反映软件功能规模的大小 D. 与软件开发成本高度相关 功能点方法是一种用于软件规模估算的方法&#xff0c;它主要用于衡量软件的功能规模大小。功能点分析不直接与软件开发…

杀毒软件火绒下载地址

杀毒软件火绒下载地址

【深度学习实战】使用深度学习模型可视化工具——Netron在线可视化深度学习神经网络

一直以来&#xff0c;对于深度学习领域的开发者&#xff0c;可视化模型都是非常迫切的需求&#xff0c;今天主要介绍一款可视化工具——Netron Netron有三种使用方式&#xff1a;在线、本地安装、pip安装 今天在这里只介绍在线使用这种方式。 Netron有个官方的网站&#xff1…

SOC 阵列:创新算力的未来之路

一、SOC阵列的概念与发展历程 SOC 阵列是由多个特定功能集成电路组合在一个芯片上的系统或产品&#xff0c;包含硬件系统及嵌入式软件。从传统集成电路到 SOC 经历多个阶段&#xff0c;初期电路由分立元件组成&#xff0c;后集成到单芯片集成电路中&#xff0c;其发展遵循摩尔…

『功能项目』协程生成怪物模型【25】

打开上一篇24AssetBundle上传加载u3d模型的项目&#xff0c; 本章要做的事情是在项目运行14秒后生成一个怪物鲸鱼&#xff0c;并且怪物鲸鱼会根据路径点自动巡航 在资源商店免费下载怪物模型 重命名为MonsterWhale 创建一个空物体 重命名为Path 在子级下创建小球Sphere作为巡…

STM32CubeIMX修改库文件代码相关问题

有时候会遇到需要需要修改库文件的需求&#xff0c;比如下面两种情况&#xff1a; 1、库文件有问题 2、库文件需要修改以适配当前需求 修改库文件的目的是&#xff0c;当下次生成程序的时候直接生成修改后的&#xff0c;无需在修改库文件。 直接将修改替换至默认文件&#xff…

软件安全测试有多重要?第三方软件测试公司如何进行安全测试?

在当今信息化迅速发展的社会&#xff0c;软件的安全性越来越受到重视。近年来&#xff0c;我国的网络安全形势日趋复杂&#xff0c;各类网络攻击层出不穷&#xff0c;软件泄露、数据丢失等事件屡见不鲜。为了保障软件项目的安全性&#xff0c;安全测试必不可少。 软件安全测试…

物流计算面单物流单标签打印软件 佳易王物流管理系统下载操作教程

一、前言 物流计算面单物流单标签打印软件 佳易王物流管理系统下载操作教程 1、佳易王物流管理系统软件分两个版本&#xff0c;一个是大众版&#xff0c;一个是高级版&#xff0c;本例以大众版为例说明 软件在打印物流单的同时可以打印标签 2、软件为免安装版&#xff0c;解…

使用matplotlab绘制多条形图

##黑马程序学习 这种多个条形图放在一起的图形该怎么画呢&#xff1f; 请看以下代码 #横着的条形图 from matplotlib import pyplot as plt import matplotlib#设置显示中文 和 负号 matplotlib.rcParams[font.sans-serif][SimHei] matplotlib.rcParams[axes.unicode_minus]F…

第二百一十四节 Java反射 - Java反射字段访问

Java反射 - Java反射字段访问 我们可以使用反射在两个步骤中获取或设置字段。 获取字段的引用。要读取字段的值&#xff0c;请在字段上调用getXxx()方法&#xff0c;其中Xxx是字段的数据类型。要设置字段的值&#xff0c;请调用相应的setXxx()方法。 以相同的方式访问静态和实…

CCF-CSP 2024 --重塑矩阵1,2c语言题解

创作想法是因为像我当初大一时候想参加一些比赛但是奈何只学了c和c相关数据结构&#xff0c;但是对于许多竞赛的题目的题解往往都是c或者其他面向对象的编程语言&#xff0c;让我们难以在c语言基础上入手这些比较复杂的题目。 创造的目的是为了帮助各位同时提高我对c语言编程的…