【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.hqwc.cn/news/102738.html

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

相关文章

《人生苦短,我学Python》——列表(List)

昨天&#xff0c;我们学习了一种数据结构——元组。今天我们将学习另一种数据结构——列表。 列表又叫List&#xff0c;与元组的结构类似&#xff0c;也可以用于存储多个类型的数据。接下来&#xff0c;我们一起学习列表的用法–> 文章目录 一、要点先知&#xff1a;二、基…

基于 Flink CDC 构建 MySQL 和 Postgres 的 Streaming ETL

官方网址&#xff1a;https://ververica.github.io/flink-cdc-connectors/release-2.3/content/%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B/mysql-postgres-tutorial-zh.html官方教程有些坑&#xff0c;经过自己实测&#xff0c;记录个笔记。 服务器环境&#xff1a; VM虚拟机&am…

护航数字政府建设,美创科技成为“数字政府建设赋能计划”成员单位

近日&#xff0c;“2023软博会-软件驱动数字政府创新发展论坛”顺利召开&#xff0c;本次论坛由中国信息通信研究院、中国通信标准化协会承办&#xff0c;中国通信标准化协会云计算标准和开源推进委员会、数字政府建设赋能计划支持。 天津市工业和信息化局总经济师杨冬梅、中国…

Paimon+StarRocks 湖仓一体数据分析方案

本文整理自阿里云高级开发工程师曾庆栋&#xff08;曦乐&#xff09;在 Streaming Lakehouse Meetup 分享的内容&#xff0c;深入探讨了传统数据仓库分析、PaimonStarRocks湖仓一体数据分析、StarRocks 与 Paimon 的协同使用方法与实现原理&#xff0c;以及StarRocks 社区湖仓分…

无涯教程-JavaScript - DELTA函数

描述 DELTA函数测试两个值是否相等。如果number1 number2,则返回1&#xff1b;否则返回1。否则返回0。 您可以使用此功能来过滤一组值。如,通过合计几个DELTA函数,您可以计算相等对的计数。此功能也称为Kronecker Delta功能。 语法 DELTA (number1, [number2])争论 Argum…

python调用GPT实现:智能用例生成工具

工具作用&#xff1a; 根据输入的功能点&#xff0c;生成通用测试点 实现步骤 工具实现主要分2个步骤&#xff1a; 1.https请求调用Gpt,将返回响应结果保存为.md文件 2.用python实现 将 .md文件转换成.xmind文件 3.写个简单的前端页面&#xff0c;调用上述步骤接口 详细代…

table 单元格中嵌套子表格 样式撑开问题

如图&#xff0c;表格中的td嵌套表格&#xff0c;里边表格把外层撑开&#xff0c;不能按100%显示&#xff1b; 解决办法 给父级table 加一个table-layout:fixed;样式

(其他) 剑指 Offer 46. 把数字翻译成字符串 ——【Leetcode每日一题】

❓ 剑指 Offer 46. 把数字翻译成字符串 难度&#xff1a;中等 给定一个数字&#xff0c;我们按照如下规则把它翻译为字符串&#xff1a;0 翻译成 “a” &#xff0c;1 翻译成 “b”&#xff0c;……&#xff0c;11 翻译成 “l”&#xff0c;……&#xff0c;25 翻译成 “z”。…

cpolar内网穿透

目录 一、引言二、什么是cpolar三、内网穿透四、如何使用cpolar1、下载cpolar软件安装包2、注册cpolar账号3、使用cpolar 一、引言 当我们完成了一个tomcat的web项目之后&#xff0c;如果我们想让其他电脑访问到这个项目&#xff0c;我们可以让其他电脑和本机连接到同一个局域…

leetcode刷题--栈与递归

文章目录 1. 682 棒球比赛2. 71 简化路径3. 388 文件的最长绝对路径4. 150 逆波兰表达式求值5. 227. 基本计算器II6. 224. 基本计算器7. 20. 有效的括号8. 636. 函数的独占时间9. 591. 标签验证器10. 32.最长有效括号12. 341. 扁平化嵌套列表迭代器13. 394.字符串解码 1. 682 棒…

MySQL 如何避免 RC 隔离级别下的 INSERT 死锁?

本文分析了 INSERT 及其变种&#xff08;REPLACE/INSERT ON DUPLICATE KEY UPDATE&#xff09;的几个场景的死锁及如何避免。 作者&#xff1a;张洛丹&#xff0c;DBA 数据库技术爱好者~ 爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联系小编…

win11和虚拟机上的ubuntu系统共享文件夹

出发点&#xff1a;有时候需要在虚拟机内和win11系统之间进行文件拷贝操作&#xff0c;但是虚拟机内的Vmware Tools不好用 解决方式 开启虚拟机共享文件夹 在虚拟机的Ubuntu系统内的终端命令行输入以下命令 sudo /usr/bin/vmhgfs-fuse .host:/SVMware /mnt/win -o subtype…