leetCode 647.回文子串 动态规划 + 优化空间 / 中心扩展法 + 双指针

news2025/7/10 17:21:24

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

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。回文字符串 是正着读和倒过来读一样的字符串。子字符串 是字符串中的由连续字符组成的一个序列。具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。


示例 1:

输入:s = "abc"
输出:3
解释:三个回文子串: "a", "b", "c"

示例 2:

输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

>>思路和分析

回文子串:讲究的是这个字符串里边左右两边是对称的左右两边的元素是相同的。如果只判断这个字符串的最左面和最右面这两个元素相同的情况下,还知道中间的子串已经是回文的,那么就可以直接判断整个字符串它就是回文子串。

也就是说,如果在[i+1,j-1]范围的子串是一个回文串,再向两边拓展遍历的时候,那只需要判断两边这两个元素是否相同就可以了若相同,dp[i][j]是回文串

>>动规五部曲

1.确定dp数组以及下标的含义

  • dp[i][j]:表示区间范围[i,j]的子串是否为回文子串。如果是,则dp[i][j] = true,否则为false

2.确定递推公式

s[i] ≠ s[j],肯定不是回文子串,那么 dp[i][j] = false;(由于dp[i][j]初始化为false,故此种情况不需要操作)

s[i] == s[j],分情况讨论: 

  • 情况1:j - i > 1时,依赖 dp[i+1][j-1] = true

  • 情况2:i = j,表指向同一个字符,例如 b ,此时 dp[i][j] = true,因为 b 是一个回文子串
  • 情况3:j - i = 1; 例如 dd ,无法加入情况1讨论,因为无法依赖 dp[i+1][j-1] j-1=i,i+1=j,此时已经不满足dp数组中[i,j]闭区间这里的 j>=i 了。所以需要单独讨论。
    • dd 这中情况在s[i] == s[j]下,也是回文子串,所以dp[i][j] = true

分析完三种情况,递推公式如下:

// result 用来统计回文子串的数量。
if (s[i] == s[j]) {
    if (j - i <= 1) { // 情况一 和 情况二
        result++;
        dp[i][j] = true;
    } else if (dp[i + 1][j - 1]) { // 情况三
        result++;
        dp[i][j] = true;
    }
}

3.dp 数组初始化

  • dp[i][j]初始化为false

4.确定遍历顺序

一定要从下到上,从左到右遍历,这样能保证dp[i+1][j-1]是经过计算得来的

也可以是优先遍历列,然后遍历行,其实道理都一样,都是为了在使用dp[i+1][j-1]时能确保都经过计算了(可参考这篇文章:647. 回文子串 - 力扣(LeetCode))

5.举例推导dp数组

左边图是dp数组初始化,在填dp数组只会对右上三角进行数据更新,所以右边的图我就不画左下三角的0了。从图中,可得知有9个true,即有9个回文子串。还可以得知另一个信息,那就是回文子串有:"a","b","d","d","b","a","dd","bddb","abddba"

注:"dd"是回文子串的信息记录在(2,3)这个坐标,"bddb"是回文子串的信息记录在(1,4)这个坐标,"abddba"是回文子串的信息记录在(0,5)这个坐标,若为true,则该子串为回文。

(2,3),(1,4),(0,5)在左对角线上,所以观察的时候可以瞄准这条线上的坐标,分析信息

注:要明确和清晰dp[i][j] 表示区间范围[i,j]的子串是否为回文子串

  • 左图的回文子串有:"a","a","d","b","a","a","aa","aa"
  • 右图的回文子串有:"a","a","d","d","a","a","aa","dd","adda","aaddaa","aa"

(1)二维dp

class Solution {
public:
    int countSubstrings(string s) {
        vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));
        int result = 0;
        for (int i = s.size() - 1; i >= 0; i--) {  // 注意遍历顺序
            for (int j = i; j < s.size(); j++) {
                if (s[i] == s[j]) {
                    if (j - i <= 1) { // 情况一 和 情况二
                        result++;
                        dp[i][j] = true;
                    } else if (dp[i + 1][j - 1]) { // 情况三
                        result++;
                        dp[i][j] = true;
                    }
                }
            }
        }
        return result;
    }
};
  • 简洁写法:
class Solution {
public:
    int countSubstrings(string s) {
        vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));
        int result = 0;
        for (int i = s.size() - 1; i >= 0; i--) {
            for (int j = i; j < s.size(); j++) {
                if (s[i] == s[j] && (j - i <= 1 || dp[i + 1][j - 1])) {
                    result++;
                    dp[i][j] = true;
                }
            }
        }
        return result;
    }
};
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n^2)

(2)二维dp 优化空间

class Solution {
public:
    int countSubstrings(string s) {
        vector<vector<bool>> dp(2,vector<bool>(s.size(),false));
        int result = 0;
        for(int i=s.size()-1;i>=0;i--) {
            for(int j=i;j<s.size();j++) {
                if(s[i] == s[j] && (j-i <= 1 || dp[(i+1)%2][j-1])) {
                    dp[i%2][j] = true;
                    result++;
                }else {
                    dp[i%2][j] = false;
                }
            }
        }
        return result;
    }
};
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n)

(3)一维dp 优化空间

class Solution {
public: 
   int countSubstrings(string s) {
        vector<bool> dp(s.size(),false);
        int result = 0;
        for(int i=s.size()-1;i>=0;i--) {
            // int pre = dp[s.size()-1];
            bool pre = false;
            for(int j=i;j<s.size();j++) {
                bool tmp = dp[j];
                if(s[i] == s[j] && (j-i <= 1 || pre)) {
                    dp[j] = true;
                    result++;
                }else{
                    dp[j] = false;
                }
                pre = tmp;
            }
        }
        return result;
    }
};
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n)

>>其他解法:暴力解法 双指针

(1)暴力解法

  • 两层循环,考察所有子串,判断是否是回文串
class Solution {
public:
    bool isPalindrome(string s) {
        int i = 0;
        int j = s.size()-1;
        while(i < j) {
            if(s[i] != s[j]) return false; 
            i++;
            j--;
        }
        return true;
    }
    // 时间复杂度:O(n^3),空间复杂度:O(1)
    int countSubstrings(string s) {
        int count=0;
        // 两层循环,考察所有子串,判断是否是回文串
        for (int i = 0; i < s.size(); i++) {
            for (int j = i; j < s.size(); j++) {
                if (isPalindrome(s.substr(i, j + 1 - i))) count++;
            }
        }               
        return count;
    }
};
  • 时间复杂度:O(n^3)
  • 空间复杂度:O(1)

(2)双指针「中心扩展法」

上文提到:回文字符串关于中心对称的字符串基于对称性的特点,可以采取向两边扩展的方法,得到回文子串

(1)以单个字符为中心(如果回文长度是奇数,那么回文中心是一个字符;

上图字符串的中心是 c ,同时向左向右扩展一格,可以得到子串是 aca ,发现该扩展子串符合回文的性质。那么就继续向左向右扩展一格,得到子串 bacad ,不符合回文的性质,停止!

因此,以 c 为中心,可以得到的回文子串有两个 c aca 

注:由于以单个字符为中心,会遗漏掉偶数回文子串的情况,故还有下文所讲的以两个字符为中心,一起来看看吧~

(2)以两个字符为中心(如果回文长度是偶数,那么中心是两个字符

总结:「中心扩展法」的思想就是遍历以一个字符或两个字符为中心可得到的回文子串


举个栗子s = "aabacabaa"

  • (1)以单个字符为中心(如果回文长度是奇数,那么回文中心是一个字符;

  • (2)以两个字符为中心(如果回文长度是偶数,那么中心是两个字符 

故 result = 15 + 2 = 17 

class Solution {
public:
    int countSubstrings(string s) {
        int result = 0;
        for (int i = 0; i < s.size(); i++) {
            result += extend(s, i, i, s.size()); // 以i为中心(以单个字母为中心的情况)
            result += extend(s, i, i + 1, s.size()); // 以i和i+1为中心(以两个字母为中心的情况)
        }
        return result;
    }
    int extend(const string& s, int i, int j, int n) {
        int res = 0;
        while (i >= 0 && j < n && s[i] == s[j]) {
            i--;
            j++;
            res++;
        }
        return res;
    }
};
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

拓展学习中心点的个数:2 * len - 1

  • aca 5 中心点,分别是 a、c、a、ac、ca
  • acca 7 中心点,分别是 a、c、c、a、ac、cc、ca

参考和推荐文章、视频:

代码随想录 (programmercarl.com)icon-default.png?t=N7T8https://www.programmercarl.com/0647.%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2.html#%E6%80%9D%E8%B7%AF

动态规划,字符串性质决定了DP数组的定义 | LeetCode:647.回文子串_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV17G4y1y7z9/?spm_id_from=pageDriver&vd_source=a934d7fc6f47698a29dac90a922ba5a3

647. 回文子串 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/palindromic-substrings/solutions/380130/shou-hua-tu-jie-dong-tai-gui-hua-si-lu-by-hyj8/

647. 回文子串 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/palindromic-substrings/solutions/1/liang-dao-hui-wen-zi-chuan-de-jie-fa-xiang-jie-zho/

来自代码随想录的课堂截图:

实战篇->我的下一篇文章: leetCode 5. 最长回文子串 动态规划 + 优化空间 / 中心扩展法 + 双指针-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_41987016/article/details/133895681?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22133895681%22%2C%22source%22%3A%22weixin_41987016%22%7D

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

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

相关文章

Springboot-MyBatisPlus-01

一 创建项目&#xff0c;选择spring boot 初始化&#xff0c;配置相关信息 第五步创建实体类 二 快速开发实体类的jar包--lombok <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12<…

Pycharm中终端不显示虚拟环境名解决方法

文章目录 一、问题说明&#xff1a;二、解决方法&#xff1a;三、重启Pycharm 一、问题说明&#xff1a; Pycharm中打开项目配置完需要的虚拟环境后&#xff0c;在Terminal&#xff08;终端&#xff09;中无法切换及显示当前需要运行代码的虚拟环境。 比如以下一种情况&#…

TOUGH2软件教程、CO2地质封存

TOUGH系列软件是由美国劳伦斯伯克利实验室开发的&#xff0c;旨在解决非饱和带中地下水、热运移的通用模拟软件。和传统地下水模拟软件Feflow和Modflow不同&#xff0c;TOUGH系列软件采用模块化设计和有限积分差网格剖分方法&#xff0c;通过配合不同状态方程&#xff08;EOS模…

人体分割模型ACE2P与M2FP,解析人脸人体各部件属性,语义化分析

前言 在做某任务的时候&#xff0c;需要对人物图片进行预处理。 预处理的要求就是要将图片中的人物各部件分割出来&#xff0c;标识为各种不同颜色&#xff0c;比如脸部为蓝色&#xff0c;脖颈部位绿色&#xff0c;其他地方为红色 最初任务使用的PaddleSeg中基于CelebAMask-HQ…

C# RestoreFormer 图像(人脸面部)修复

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms;namespace 图像修复 {pu…

优维低代码实践:片段

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 优维…

Unity3D 基础——使用 Vector3.Distance 计算两个物体之间的距离

Vector3-Distance - Unity 脚本 APIhttps://docs.unity.cn/cn/current/ScriptReference/Vector3.Distance.html 1.在场景中新建两个 Cube 立方体&#xff0c;在 Scene 视图中将两个 Cude的位置错开。 2.新建 C# 脚本 Distance.cs&#xff08;写完记得保存&#xff09; using …

PS 学习笔记

书籍&#xff1a;Photoshop 2022从入门到精通-敬伟-微信读书 1. PS 常用快捷键 复位右侧基本工作栏&#xff1a;【窗口】-【工作区】- 【复位基本功能】 Ctrl 鼠标滚轮&#xff1a;主界面图片左右滚动Shift 鼠标滚轮&#xff1a;主界面图片上下滚动Alt 鼠标滚轮&#xff1…

StarUML的介绍与使用

文章目录 简介视图StarUML创建视图类图用例图时序图 简介 UML&#xff1a;统一建模语言&#xff0c;用模型元素组成的不同视图从各个维度来描述系统 StarUML为常用系统建模工具之一 视图 常见视图的概念可参考&#xff1a;UML常见的几种视图 包括&#xff1a;用例图、顺序图…

联想G50笔记本直接使用F键功能(F1~F12)需要在BIOS设置关闭热键功能可以这样操作!

如果开启启用热键模式按F1就会出现FnF1的效果&#xff0c;不喜欢此方式按键的用户可以进入BIOS设置界面停用热键模式即可。 停用热键模式方法如下&#xff1a; 1、重新启动笔记本电脑&#xff0c;当笔记本电脑屏幕出现Lenovo标识的时候&#xff0c;立即按FnF2进入BIOS设置界面…

ssm+vue的养老院老人健康监护平台(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的养老院老人健康监护平台&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

从入门到进阶 之 ElasticSearch 节点配置 集群篇

&#x1f339; 以上分享 ElasticSearch 安装部署&#xff0c;如有问题请指教写。&#x1f339;&#x1f339; 如你对技术也感兴趣&#xff0c;欢迎交流。&#x1f339;&#x1f339;&#x1f339; 如有需要&#xff0c;请&#x1f44d;点赞&#x1f496;收藏&#x1f431;‍&a…

基于SSM+Vue的咖啡销售系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

全面中文大语言模型评测来啦!香港中文大学最新研究

ChatGPT 的一声号角吹响了2023年全球大语言模型的竞赛。 2023年初以来&#xff0c;来自工业界和研究机构的各种大语言模型层出不穷&#xff0c;特别值得一提的是&#xff0c;中文大语言模型也如雨后春笋般&#xff0c;在过去的半年里不断涌现。 与此同时&#xff0c;和如何训…

Android 10 中的隐私权变更

Android 10 中的隐私权变更 重大变更外部存储访问权限范围限定为应用文件和媒体在后台运行时访问设备位置信息需要权限以 Android 9 或更低版本为目标平台时自动授予访问权限在设备升级到 Android 10 后访问针对从后台启动 Activity 的限制标识符和数据移除了联系人亲密程度信息…

Go语言入门心法(六): HTTP面向客户端|服务端编程

Go语言入门心法(一): 基础语法 Go语言入门心法(二): 结构体 Go语言入门心法(三): 接口 Go语言入门心法(四): 异常体系 Go语言入门心法(五): 函数 一:go语言面向web编程认知 Go语言的最大优势在于并发与性能,其性能可以媲美C和C,并发在网络编程中更是至关重要 使用http发送请…

hal开发之hidl/aidl支持的绑定式直通式详细讲解

为啥有hidl呢&#xff1f; 这个问题其实网络上答案比较多&#xff0c;属于android想要让厂商快速升级解耦制定的&#xff0c;即把原来系统framework和厂商耦合的hal在同一个个system.img进行剥离开&#xff0c;把厂商相关的放到vendor.img&#xff0c;aosp系统公共部分framewo…

ros_rtsp订阅Image类型topic转换为rtsp视频流

文章目录 一、安装环境二、在catkin工作空间中构建三、设置流四、推出视频流五、验证视频流1、安装vlc拉流2、安装gstreamer拉流3、安装FFmpeg拉流 一、安装环境 ROS gstreamer development libs&#xff0c;包括base、good、bad和rtspserver: sudo apt-get install libgstre…

成功实施自动化测试的优点

目录 什么是自动化测试&#xff1f; 自动化测试的好处 测试执行7*24 回归测试 可重用性 节省您很多时间 降低成本更好地利用人力 左移测试做得更好&#xff01; 解放手动测试 最大化测试覆盖率 监控服务 复杂而冗长的测试方案 结论 随着技术的发展&#xff0c;保证…

国产FiRa认证低功耗UWB系统级SoC精准定位方案芯片

目录 什么是"UWB技术"国产UWB方案芯片特性国产低功耗UWB SoC芯片特性 随着物联网、无线通信等技术的不断发展&#xff0c;UWB作为一种超宽带通信技术&#xff0c;逐渐在精准定位、智能家居、汽车电子、智能制造等领域崭露头角。 什么是"UWB技术" UWB&…