【leetcode 力扣刷题】回文串相关题目(KMP、动态规划)

回文串相关题目

  • 5. 最长回文子串
    • 动态规划
    • 中心扩展算法
  • 214. 最短回文串
  • 336. 回文对

5. 最长回文子串

题目链接:5. 最长回文子串
题目内容:在这里插入图片描述
题目就是要我们找s中的回文子串,还要是最长的。其实想想,暴力求解也行……就是遍历所有的子串,同时判断是不是回文串,是的话再和记录的最大长度maxlen比较,如果更长就更新。时间复杂度直接变成O(n^3)。

动态规划

优化的点在于,假设子串s[i~j]已经不是回文串了,s[i-1~j+1]也不是回文串,就不用再去判断是否是回文串了。用动态规划求解,dp[i][j]为true或者false,表示s[i~j]子串是or不是回文串,dp更新过程:

  • dp[i][j] = true需要dp[i+1][j-1] = true同时s[i] = s[j];【注意i+1 >= j-1】
  • 如果dp[i+1][j-1] = false,dp[i][j]直接为false;
  • 如果s[i] != s[j],直接false;
  • dp[i][i] = true;
    代码实现(C++):
class Solution {
public:string longestPalindrome(string s) {int n = s.size();//如果s是空或者只有一个字符直接返回s,本身就是回文串if(n < 2)return s;//dp记录s所有子串是否是回文串vector<vector<bool>> dp(n,vector<bool>(n));//单字符子串s[i]是回文串for(int i = 0; i < n ; i++)dp[i][i] = true;//记录目前最长的子串长度和开始的下标int maxLen = 1, idx = 0;//L是子串的长度,按照长度来找子串for(int L = 2; L <= n; L++){//子串开始下标for(int begin = 0; begin <= n-L; begin++){//子串结束下标int end = L - 1 + begin;//判断当前子串是否是回文子串if(s[begin] != s[end])dp[begin][end] = false;else{if(L <= 3)dp[begin][end] = true;elsedp[begin][end] = dp[begin+1][end-1];}//如果当前子串是回文子串,其长度和maxlen对比if(dp[begin][end] && L > maxLen){maxLen = L;idx = begin;}}}//返回最长回文子串return s.substr(idx, maxLen);}
};

动态规划也是判断所有子串是否是回文串,但是相较于暴力求解,用dp来存储每个子串是否是回文串,一个子串s[i~j]是否是回文串可以直接通过dp[i+1][j-1]得到,时间复杂度是O(1),因此整体时间复杂度是O(n^2)。同时dp需要额外的空间,空间复杂度是O(n^2)。
注意上述遍历子串,最外层是通过子串长度来控制的,如果外层是子串开始下标begin,内层是子串结束下标end,dp[begin][end]根据dp[begin+1][end-1]决定是true还是false,需要先有dp[begin+1][end-1],即dp[begin+1]这一行要先求得值,begin要从大到小:

//注意begin从大到小,从后往前
for(int begin = n - 2; begin >= 0; begin --){for(int end = begin + 1; end < n; end ++){if(s[begin] != s[end])dp[begin][end] = false;else{if(end - begin < 3)dp[begin][end] = true;elsedp[begin][end] = dp[begin+1][end-1];}if(dp[begin][end] && end - begin + 1 > maxLen){maxLen = end - begin + 1;idx = begin;}}
}

中心扩展算法

动态规划需要dp来存储所有子串是否是回文串,其实是并不需要的。如果以一个字符串作为中心,然后朝两边扩展,s[i~j]变成s[i-1~j+1],s[i-1~j+1]是否是回文串直接依赖于s[i~j]的,如果s[i~j]不是回文串了,再朝两边扩展是没有意义的。这样减少了部分子串的判断,同时减少了dp这个二维数组。当s[i~j]不能朝两边扩展的时候,当前的s[i~j]就是以某个字符为中心的最长的回文子串,此时与maxlen比较,判断是否更新maxlen即可。
这样的中心就是s中的n个字符。但是需要注意的是,如果只是以这个字符作为中心,从s[i~i]开始,,遍历的永远都是长度为奇数的子串。还需要遍历长度为偶数的子串,即从s[i~i+1]这样的子串开始。代码如下(C++):

class Solution {
public://查找以一个字符或者两个字符为中心的最长回文子串pair<int, int> expandAroundCenter(const string& s, int left, int right) {while (left >= 0 && right < s.size() && s[left] == s[right]) {--left;++right;}//返回左右下标return {left + 1, right - 1};}string longestPalindrome(string s) {int start = 0, end = 0;//所有字符都作为中心字符去查找最长的回文子串for (int i = 0; i < s.size(); ++i) {auto [left1, right1] = expandAroundCenter(s, i, i);auto [left2, right2] = expandAroundCenter(s, i, i + 1);//更新if (right1 - left1 > end - start) {start = left1;end = right1;}//更新if (right2 - left2 > end - start) {start = left2;end = right2;}}return s.substr(start, end - start + 1);}
};

这里时间复杂度也是O(n^2),但是提交的时候运行时间,比动态规划少了20倍……
在这里插入图片描述

214. 最短回文串

题目链接:214. 最短回文串
题目内容:
在这里插入图片描述
题目的意思是要在字符串s的前面添加字符(字符数量≥1,可以说是加一个字符串s’),添加字符的目的是为了让s变成一个回文串。 另外需要最终的s’+s是所有答案中的最短的,也就是要添加的s’最短。
要使得s’+s是回文串很简单,直接把s的逆序加在s的前面,肯定是个回文串;或者把s第一个字符后的子串逆序加在s前,也肯定是回文串,是以s首字符为回文中心的。
在这里插入图片描述
上面的方法是可以得到回文串的,但是要怎么样才能使得最终的回文串更短呢。
回文串有一个回文中心,回文中心两边是长度相等的、一段的逆序与另外一段完全相同的两段子串。现在在s的前面加一些字符能够使得s’+s是回文串,那么反过来想,删除s中末尾一段与s’的逆序相同的子串,剩下的子串也是回文串。 所以只要能够在s中找到一段以s首字符开始的最长的回文串,就能保证在此基础上,将s中除这个回文子串剩下的子串,逆序加在s前面得到的回文串是最短的。
在这里插入图片描述那么我们要怎么寻找s中是回文的前缀呢?假设这个回文子串是s1,s除s1外的子串s-s1用s2表示,s^是s的逆序(反转)后的字符串,s1在s^中其实就是后缀,由于s1是回文串,所以s1 = reverse(s1)。所以把s^看作是查找串,s看作是模式串,两者其做字符串匹配,用kmp算法,最终当s^遍历到最后一个字符的时候,s是第i个字符,那么0~i这段子串就是s1,即最长的前缀回文串:
在这里插入图片描述
整体解题步骤是:

  • 找s1:s反转后的字符串s^作为查找串,s作为模式串,用kmp算法去做字符串匹配;算法结束的时,即遍历到s^最后一个字符时,对应s中第i个字符,0~i即为查找的最长前缀回文串;
  • 将s-s1反转,并加在s前面,即得到了答案;
    代码实现(C++):
class Solution {
public:int strStr(string haystack, string needle) {int n = needle.size();vector<int>  next(n, 0);//next数组中存的是对应下标处子串【包括下标位置】的最长前后缀的长度for(int i = 1; i < n; i++){int j = next[i-1];while(j>0 && needle[j] != needle[i])j = next[j-1];if(needle[i] == needle[j])j++;next[i] = j;}int pos = 0, j = 0;while(pos < haystack.size()){while(j>0 && haystack[pos] != needle[j])j = next[j-1];if(haystack[pos] == needle[j]){pos++;j++;} elsepos++;                  }//循环结束时,pos=haystack.size(), j对应子串长度,而不是结束下标,下标为j-1return j;}string shortestPalindrome(string s) {//s为空或者只有一个字符的时候,直接返回if(s.empty() || s.size() == 1)return s;string re_s = s;reverse(re_s.begin(),re_s.end()); //得到s的逆序re_s//idx其实是前缀回文子串s1的长度int idx = strStr(re_s,s);//即s一整个是回文串if(idx == s.size())return s;//反转s2,实际就是re_s前面一截,并加在s前面return re_s.substr(0, s.size() - idx) + s;}
};

这道题目在找s中最长前缀回文串的时候,没有使用上面题目的动态规划,是因为本题很明确,这个回文串是s的前缀,是从s第一个字符开始的子串。而要找s的最长回文子串,这个子串开始的位置是不知道的。
另外,如果题目换成在s的后面加上s’,使得s+s’是最短回文串,同样的方法,不过kmp的时候,s是查找串,s^是模式串。

336. 回文对

题目链接:336. 回文对
题目内容:
在这里插入图片描述
理解题意,是要在words中找到两个字符串words[i]、words[j],使得words[i] + words[j]是回文串。这题目不是和上面题目很像嘛!可以分情况讨论:

  • words[i]和words[j]互为对方的逆序,比如words[i] = “abcd”,words[j] =“dcba”,那么words[i] + words[j]和words[j] + words[i]都是回文的;
  • words[i]前缀本身是回文,words[j]是words[i]以回文前缀下标结束点m为开始的后缀的逆序,那么words[j] + words[i]是回文的;比如words[i] = “abcbadef”,words[j] = “fed"或者"fed”;
  • words[i]后缀本身是回文,words[j]是words[i]中回文后缀下标开始点m为结束的前缀的逆序,那么words[i] + words[j]是回文的;比如words[i] = “defabcba”,words[j] = “fed”;

还没做,代码待更……

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

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

相关文章

Python编程的八大魔法库

Python是一门广受欢迎的编程语言&#xff0c;其生态系统丰富多彩&#xff0c;拥有许多令人惊叹的依赖库&#xff0c;可以帮助程序员们在各种领域中创造出令人瞠目结舌的应用。在这篇文章中&#xff0c;我们将探讨Python编程的十大神奇依赖库&#xff0c;它们像魔法一样&#xf…

jmeter 计数器Counter

计数器可以用于生成动态的数值或字符串&#xff0c;以模拟不同的用户或数据。 计数器通常与用户线程组结合使用&#xff0c;以生成不同的变量值并在测试中应用。以下是计数器的几个常用属性&#xff1a; 变量前缀&#xff08;Variable Name Prefix&#xff09;&#xff1a;定义…

G. The Morning Star

Problem - G - Codeforces 思路&#xff1a;想了挺长时间的&#xff0c;一直没想到一个简便的方法在瞎搞。我们发现对于某个点来说&#xff0c;其他的点如果能够跟他匹配&#xff0c;那么一定在这8个方向上&#xff0c;而同时这8个方向其实对应这4条直线&#xff0c;假设点为(x…

静态路由 网络实验

静态路由 网络实验 拓扑图初步配置R1 ip 配置R2 ip 配置R3 ip 配置查看当前的路由表信息查看路由表信息配置静态路由测试 拓扑图 需求&#xff1a;实现 ip 192.168.1.1 到 192.168.2.1 的通信。 初步配置 R1 ip 配置 system-view sysname R1 undo info-center enable # 忽略…

C语言经典100例题(55)--从一个整数a中把从右端开始的4-7位取出来

目录 题目 问题分析 右移操作符 左移操作符 方法一 方法二 运行结果 题目 用c语言从一个整数a中把从右端开始的4-7位取出来 问题分析 右移操作符 右移操作符是一种位运算符&#xff0c;用于将二进制数向右移动指定的位数。它通常用符号" >> "表示…

W11下CMake MinGW配置OpenCV和Qt

&#x1f482; 个人主页:风间琉璃&#x1f91f; 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 前言 前几天将cuda版本的opencv给编译成功了&#xff0c;当时用的VS的MSVC&…

day37 线程

一、线程安全 二、多线程并发的安全问题 当多个线程并发操作同一临界资源 由于线程切换实际不确定 导致操作顺序出现混乱 产生的程序bug 严重时出现系统瘫痪 临界资源 &#xff1a;操作该资源的完整流程同一时间只能被单一线程操作的资源 多线程并发会出现的各种问题、 如…

聊聊 HTMX 吧

写在前面 最近看了几篇关于 htmx 的文章&#xff0c;自己也去看了一眼官网&#xff0c;也去油管看了一下当时 htmx 发布会的时候他们的演示&#xff0c;下面说几点我对这个所谓的新型起来的技术的看法&#xff0c; 他的来源是什么 首先说一下他虽然是一个新型的技术&#xff0c…

Java 多线程系列Ⅶ(线程安全集合类)

线程安全集合类 前言一、多线程使用线性表二、多线程使用栈和队列三、多线程下使用哈希表 前言 在数据结构中&#xff0c;我们学习过 Java 的内置集合&#xff0c;但是我们知道&#xff0c;我们学过的大多数集合类都是线程不安全的&#xff0c;少数如 Vector&#xff0c;Stack…

小程序分销机制介绍,小程序二级分销功能有哪些?

为什么有越来越多的用户选择使用小程序&#xff1f;跟“高大上”的APP相比&#xff0c;小程序不仅可以减少下载安装的复杂流程&#xff0c;还具备操作便捷、沉淀私域数据的优势。蚓链分销小程序具备裂变二维码、实时分佣、分销身份升级、层级分佣、商品个性化佣金设定等功能&am…

TortoiseSVN 详细操作指南

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 热爱技术的小郑 1、引言 考虑以下几种情况&#xff1a; 你是否在一个…

golang面试题:json包变量不加tag会怎么样?

问题 json包里使用的时候&#xff0c;结构体里的变量不加tag能不能正常转成json里的字段&#xff1f; 怎么答 如果变量首字母小写&#xff0c;则为private。无论如何不能转&#xff0c;因为取不到反射信息。如果变量首字母大写&#xff0c;则为public。 不加tag&#xff0c…

【Spring Cloud系统】- 轻量级高可用工具Keepalive详解

【Spring Cloud系统】- 轻量级高可用工具Keepalive详解 文章目录 【Spring Cloud系统】- 轻量级高可用工具Keepalive详解一、概述二、Keepalive分类2.1 TCP的keepalive2.2 HTTP的keep-alive2.3 TCP的 KeepAlive 和 HTTP的 Keep-Alive区别 三、nginx的keepalive配置3.1 nginx保持…

连接云-边-端,构建火山引擎边缘云网技术体系

近日&#xff0c;火山引擎边缘云网络产品研发负责人韩伟在LiveVideoStack Con 2023上海站围绕边缘云海量分布式节点和上百T的网络规模&#xff0c;结合边缘云快速发展期间遇到的各种问题和挑战&#xff0c;分享了火山引擎边缘云网的全球基础设施&#xff0c;融合开放的云网技术…

数据结构——七大排序[源码+动图+性能测试]

本章代码gitee仓库&#xff1a;排序 文章目录 &#x1f383;0. 思维导图&#x1f9e8;1. 插入排序✨1.1 直接插入排序✨1.2 希尔排序 &#x1f38a;2. 选择排序&#x1f38b;2.1 直接选择排序&#x1f38b;2.2 堆排序 &#x1f38f;3. 交换排序&#x1f390;3.1 冒泡排序&#…

Qt应用开发(基础篇)——工具按钮类 QToolButton

一、前言 QToolButton类继承于QAbstractButton&#xff0c;该部件为命令或选项提供了一个快速访问按钮&#xff0c;通常用于QToolBar中。 按钮基类 QAbstractButton QToolButton是一个特殊的按钮&#xff0c;一般显示文本&#xff0c;只显示图标&#xff0c;结合toolBar使用。它…

【图文并茂】c++介绍之队列

1.1队列的定义 队列&#xff08;queue&#xff09;简称队&#xff0c;它也是一种操作受限的线性表&#xff0c;其限制为仅允许在表的一端进行插入操作&#xff0c;而在表的另一端进行删除操作 一些基础概念&#xff1a; 队尾&#xff08;rear&#xff09; &#xff1a;进行插…

MFC新建内部消息

提示&#xff1a;记录一下MFC新建内部消息的成功过程 文章目录 前言一、第一阶段二、第二阶段三、第三阶段总结 前言 先说一下基本情况&#xff0c;因为要在mapview上增加一个显示加载时间的功能。然后发现是要等加载完再显示时间&#xff0c;显示在主窗口。所以就是在子线程中…

开开心心带你学习MySQL数据库之节尾篇

Java的JDBC编程 各种数据库,MySQL, Oracle, SQL Server在开发的时候,就会提供一组编程接口(API) API ~~ Application Programming Interface ~~ 应用程序编程接口 计算机领域里面的一个非常常见的概念, 给你个软件,你能对他干啥(从代码层次上的) 基于它提供的这些功能,就可以写…

AJAX学习笔记5同步与异步理解

AJAX学习笔记4解决乱码问题_biubiubiu0706的博客-CSDN博客 示例 前端代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>演示AJAX同步和异步</title> </head> <body> <script…