最长回文子串:动态规划推导

news2024/11/15 4:36:00

最长回文子串:结合图形推导动态规划

题目介绍

本题可以在力扣找到,题号为5。
给你一个字符串 s,找到 s 中最长的 回文子串。

示例 1:

输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:

输入:s = “cbbd”
输出:“bb”

提示:

1 <= s.length <= 1000
s 仅由数字和英文字母组成

暴力解法

先从暴力解法入手,对于长度为n的字符串,总共有 (n+1) * n / 2 种组合,我们可以找出每种组合,然后判断其是否是回文串,最后与当前的最大值进行比较,若新串更大,就更新答案。

我们先写一个判断回文串的函数,用双指针判断即可:

    public static boolean isPalindrome(String s) {
        int left = 0;
        int right = s.length() - 1;
        while (left < right) {
            if (s.charAt(left) != s.charAt(right)) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }

然后我们用双重循环,遍历到每一种可能,在进行进一步判断:

class Solution {
    public String longestPalindrome(String s) {
        String resp = "";
        int ans = 0;
        int[][] dp = new int[s.length()][s.length()];
        int n = s.length();
        for (int i = 0;i < n;i++) {
            for (int j = i;j < n;j++) {
            	//获取子串
                String subString = s.substring(i,j+1);
                //判断是否要更新最长子串
                if (subString.length() > resp.length() && isPalindrome(subString)) {
                    resp = subString;
                }
            }
        }
        //返回答案
        return resp;
    }

    public boolean isPalindrome(String s) {
        int left = 0;
        int right = s.length() - 1;
        while (left < right) {
            if (s.charAt(left) != s.charAt(right)) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
}

动态规划

接下来我们可以考虑动态规划的解法,所谓动态规划,就是用之前的状态递推后面的状态。首先我们需要确定一个状态转移条件,对于一个 字符串 来说,如果有 一个子串str 为回文串,且 str的前一个字符和后一个字符也相同(比如说 “aba” 和 “aabaa”)那么就说明 [str-1,str+1] 也为回文串。我们可以定一个二维数组,将其定义为 从 i 到 j 的子串是否是回文串:

//初始化一个状态数组--布尔类型
int[][] dp = new int[n][n];
for (int i = 0;i < n;i++) {
	//对于单个字符的情况,必定为回文串
	dp[i][i] = true;
}

根据我们之前的结论,可以推出状态转移方程,即

if (s.charAt(i) == s.charAt(j) && dp[i+1][j-1] == true) {
	dp[i][j] = true;
} else {
	dp[i][j] = false;
}

可以看到,dp[i][j]的状态是基于 dp[i+1][j-1]的,即若要得到 dp[i][j],则要先得到dp[i+1][j-1]的状态,光这么描述其实还是很抽象,我们借助矩阵图来理解:
在这里插入图片描述
我们把横轴作为j,纵轴作为i,首先我们可以填充 dp[i][i],即只有一个字符的情况,都可以填充为true:
在这里插入图片描述
其次,对于j > i 的情况其实都可以不用考虑,因为起始位置是要始终小于结束位置的:
在这里插入图片描述
接着,我们推到一般情况的依赖关系,我们先任选一点,比如 dp[0][2],根据状态转移方程,我们需要参考的就是 dp[1][1] 的状态值了:
在这里插入图片描述
即,对于每一个状态,在二位图像上总是需要依赖其左下角的具体状态,所以理论上来说,只要从对线向右上填充,我们就可以得到整个dp数组的状态了:
在这里插入图片描述
对于上面的示例来说,只要我们填充了 i = j 以及 i + 1 = j 这两条对角线,我们就可以成功推导出整个dp数组的状态,接下来我们开始编程实现:

    public static String longestPalindrome(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n][n];
        //填充对角线
        for (int i = 0;i < n;i++) {
            dp[i][i] = true;
        }
        //填充 j = i + 1
        for (int i = 0;i < n-1;i++) {
            int j = i + 1;
            if (s.charAt(i) == s.charAt(j)) {
                dp[i][j] = true;
            }
        }
        //填充其余情况,按对角线顺序填充
        for (int offset = 0;offset < n-2;offset++) {
            for (int i = 0;i < n-2-offset;i++) {
                int j = i + 2 + offset;
                if (s.charAt(i) == s.charAt(j) && dp[i+1][j-1]) {
                    dp[i][j] = true;
                }
            }
        }
        //遍历dp数组,求出最长值
        int longestLength = 0;
        String resp = "";
        for (int i = 0;i < n;i++) {
            for (int j = 0;j < n;j++) {
                if (dp[i][j] && j - i + 1 > longestLength) {
                    longestLength = j - i + 1;
                    resp = s.substring(i,j+1);
                }
            }
        }
        return resp;
    }

具体实现就如上,需要注意的是,由于 dp[i][j] 依赖于 dp[i+1][j-1],即依赖于左下角的状态,所以我们填充一般状态的时候也需要按照这个顺序填充,即需要按照对角线层层填充才能得到正确答案。

我们可以与暴力做法的耗时作比较,暴力解法耗时大概在 1000ms左右,而上面这种基于动态规划的解法耗时只有 200ms左右:
在这里插入图片描述
大幅优化了耗时,不过上述解法中还有可以改进的地方,比如我们可以在填充dp数组的时候就开始寻找答案,而不是分两步走:

    public static String longestPalindrome(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n][n];
        //填充对角线
        for (int i = 0;i < n;i++) {
            dp[i][i] = true;
        }
        int longestLength = 1;
        String resp = s.charAt(0) + "";
        //填充 j = i + 1
        for (int i = 0;i < n-1;i++) {
            int j = i + 1;
            if (s.charAt(i) == s.charAt(j)) {
                dp[i][j] = true;
                if (j - i + 1 > longestLength) {
                    longestLength = j - i + 1;
                    resp = s.substring(i,j+1);
                }
            }
        }
        //填充其余情况,按对角线顺序填充
        for (int offset = 0;offset < n-2;offset++) {
            for (int i = 0;i < n-2-offset;i++) {
                int j = i + 2 + offset;
                if (s.charAt(i) == s.charAt(j) && dp[i+1][j-1]) {
                    dp[i][j] = true;
                    //将判断步骤放入填充步骤中
                    if (j - i + 1 > longestLength) {
                        longestLength = j - i + 1;
                        resp = s.substring(i,j+1);
                    }
                }
            }
        }
        return resp;
    }

这样我们就可以省去一次遍历的开销,最终优化效果到 139ms 左右:
在这里插入图片描述

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

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

相关文章

Composio:开源项目中的AI智能体任务执行利器

目录 一、引言二、Composio 简介三、Composio 的功能特性四、Composio 的应用场景五、Composio 的应用实践1、安装 Composio 核心库2、安装OpenAI3、添加 GitHub 集成4、初始化Composio工具集5、获取预配置的 GitHub 工具6、工具函数配置7、执行工具函数 六、结语 一、引言 在…

可定制化内容具体识别事物,多方位同时监管的智慧快消开源了

智慧快消视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。国产化人工智能“…

Vue实现zip压缩下载

1&#xff0c;安装依赖npm //jszip是一个用于创建、读取和编辑.zip文件的JavaScript库 https://stuk.github.io/jszip/ npm install jszip https://www.npmjs.com/package/file-saver npm install file-saver 2&#xff0c;在所需的页面中引入对应包 import JSZip from &…

3.服务注册_服务发现

文章目录 1.服务注册_服务发现1.1服务注册概念及图解介绍2.2 CAP理论2.3 常见的注册中心(了解)2.4 Eureka组件介绍2.4.1.搭建注册中心2.4.2服务注册2.4.3服务发现 大家好&#xff0c;我是晓星航。今天为大家带来的是 服务注册_服务发现 相关的讲解&#xff01;&#x1f600; 1…

自然语言常见面试题及答案(41~60)

Reply&#xff1a;面试题 获取资料下载 文章目录 41. 谈谈在自然语言处理中&#xff0c;如何评估模型的性能&#xff1f;42. 什么是语言模型&#xff08;Language Model&#xff09;&#xff1f;它在自然语言处理中的作用是什么&#xff1f;43. 如何进行文本分类任务&#xff…

外卖点餐配送系统源码的模块化设计:快速开发与迭代的秘诀

在快速发展的外卖行业中&#xff0c;点餐配送系统的开发需要具备高效、可扩展、易维护的特点。模块化设计能够有效地解决这些问题&#xff0c;通过将系统功能分解为多个独立的模块&#xff0c;使得开发团队可以快速开发和迭代每个模块&#xff0c;减少耦合度&#xff0c;提高系…

SpringBoot-读取配置文件方式

前言 Spring Boot提供了多种灵活的方式来读取配置文件&#xff0c;以适应不同的开发和部署需求&#xff0c;SpringBoot启动的时候&#xff0c;读取配置文件的时候&#xff0c;首先获取的是file:/config/文件下的配置文件&#xff0c;也就是项目下config文件里面的配置文件&…

Leetcode 216.组合总和Ⅲ 回溯+剪枝 C++实现

Leetcode 216.组合总和Ⅲ 问题&#xff1a;找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字 1 到 9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 算…

【rancher镜像】修改rancher官方镜像仓库为私有仓库

背景 在使用rancher构建k8s时&#xff0c;由于中国区网络问题经常没法访问docker的官方仓库镜像docker.io&#xff0c;而rancher在构建k8s时&#xff0c;会默认从docker.io去下载镜像&#xff0c;由于网络原因&#xff0c;构建就会存在问题&#xff0c;镜像无法下载&#xff0…

Cxx primer-chap17-Specialized Library Facilities

tuple(元组)是一个模板&#xff0c;类似于pair&#xff0c;但是支持多个member&#xff0c;其主要用于将多个数据合并成一个对象&#xff1a;不像pair访问成员是固定的&#xff08;first/second&#xff09;&#xff0c;访问tuple的成员需要使用函数模板get:bitset类比位运算方…

PDF秒变Word,你的文档编辑从此开挂!

在现代办公中&#xff0c;PDF和Word是我们最常接触的两种文件格式。PDF因其良好的兼容性和固定的格式而广受欢迎&#xff0c;但在编辑时却常常让人感到束手无策。而Word则因其强大的编辑功能成为文档处理的首选。 那么&#xff0c;如何将PDF转化为Word&#xff0c;让文档编辑更…

Linux多线程——线程的概念和控制

文章目录 线程的概念进程和线程对比 线程的控制创建线程与分配任务线程终止线程等待线程分离 pthread线程库 线程的概念 线程是我们经常听到的一个概念&#xff0c;他和进程有什么关系呢 从操作系统课本里我们可能听说过&#xff0c;线程是一个微缩版的进程&#xff0c;他拥有…

vue将二维码做成名片,并且生成图片保存

效果图 1. 安装html2canvas 首先&#xff0c;你需要在你的Vue项目中安装html2canvas。你可以通过npm或yarn来安装它&#xff1a; npm install html2canvas # 或者 yarn add html2canvas2.组件形式 2.1 创建组件 在你的Vue项目中&#xff0c;创建一个新的Vue组件&#x…

YOLO-World: Real-Time Open-Vocabulary Object Detection:实时开放词汇对象检测

YOLO系列探测器已成为高效实用的工具。然而&#xff0c;它们对预定义和训练的对象类别的依赖限制了它们在开放场景中的适用性。针对这一限制&#xff0c;我们引入了YOLO-World&#xff0c;这是一种创新方法&#xff0c;通过视觉语言建模和大规模数据集的预训练&#xff0c;增强…

深度学习入门-10

基于小土堆学习 池化层学习 池化层&#xff08;Pooling Layer&#xff09;是卷积神经网络&#xff08;CNN&#xff09;中的一种重要组件&#xff0c;它的主要作用是逐步减小数据的空间尺寸&#xff08;即高度和宽度&#xff09;&#xff0c;以减少网络中参数的数量和计算量&a…

OpenCV绘图函数(2)绘制圆形函数circle()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 绘制一个圆。 cv::circle 函数用于绘制一个给定中心和半径的简单圆或填充圆。 函数原型 void cv::circle (InputOutputArray img,Point cen…

a探索Python中的DOM操作神器:pyquery

文章目录 探索Python中的DOM操作神器&#xff1a;pyquery背景&#xff1a;为什么选择pyquery&#xff1f;pyquery是什么&#xff1f;如何安装pyquery&#xff1f;五个简单的pyquery函数使用方法场景应用&#xff1a;pyquery在实际开发中常见bug及解决方案总结 探索Python中的DO…

游泳耳机哪个牌子好?四大爆款游泳耳机实测,优缺点秒懂!

在快节奏的现代生活中&#xff0c;游泳不仅是一种锻炼身体的方式&#xff0c;更是一种放松心情、享受宁静的休闲活动。而音乐&#xff0c;作为情感的载体&#xff0c;其在游泳过程中的陪伴&#xff0c;无疑能够让人更加沉浸于水下的宁静与自由。因此&#xff0c;一款性能优越、…

积鼎科技携手潍柴动力的喷嘴雾化模拟项目荣获2024年数字仿真卓越应用奖

近日&#xff0c;积鼎科技携手潍柴动力股份有限公司&#xff08;以下简称“潍柴动力”&#xff09;&#xff0c;凭借创新的喷嘴雾化一体化模拟仿真项目&#xff0c;其成果价值赢得了行业专家的一致认可&#xff0c;成功荣获2024年度数字仿真科技奖卓越应用奖。潍柴动力作为中国…

唯创知音在线TTS方案简介及测试版操作说明

一&#xff1a;背景介绍 在物联网与智能设备日新月异的今天&#xff0c;音频传输与控制技术成为了提升用户体验的关键环节。WT2605C蓝牙音频更新方案&#xff0c;凭借其强大的功能集与高效的交互设计&#xff0c;为两轮电动车、电子锁、提示器、智能安防等多个领域带来了前所未…