最长回文子串与最长回文子序列
文章目录
- 最长回文子串与最长回文子序列
- 最长回文子串
- 题目描述
- dp解法
- 从中心扩展
- 马拉车算法
- 最长回文子序列
- 题目描述
- dp 解法
- 递归思想
最长回文子串与最长回文子序列
最长回文子串
LeetCode 链接: 5. 最长回文子串
题目描述
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
dp解法
如果我们已经知道 “bab”
是回文子串,那么很明显 “ababa”
一定是回文子串,因为这两个左右端字母是相同的。假设 S
是给定的字符串,SiS_iSi 表示 S
在位置 i
的字符
定义状态:
dp(i,j)={true,当Si...Sj是回文子串false,其它情况dp(i,j)=\begin{cases} true, & \text {当} S_i...S_j \text{是回文子串} \\ false, & \text {其它情况} \end{cases} dp(i,j)={true,false,当Si...Sj是回文子串其它情况
初始状态:
dp(i,i)=truedp(i,i)=true dp(i,i)=true
dp(i,i+1)=(Si==Sj)dp(i,i+1)=(S_i==S_j) dp(i,i+1)=(Si==Sj)
转移方程:
dp(i,j)={Si==Sj&&dp(i+1,j−1),j−i>2Si==Sj,1<=j−i<=2truej−i=0dp(i,j) = \begin{cases} S_i==S_j \ \&\& \ dp(i+1,j-1), & j-i>2 \\ S_i==S_j, & 1<=j-i<=2 \\ true & j-i=0 \end{cases} dp(i,j)=⎩⎪⎨⎪⎧Si==Sj && dp(i+1,j−1),Si==Sj,truej−i>21<=j−i<=2j−i=0
需要注意的是,求 dp(i+1,j-1)
时要保证 j-i > 2
,防止出现越界错误
public String longestPalindrome(String s) {if (s == null || s.length() == 0)return s;int n = s.length();char[] str = s.toCharArray();String res = null;boolean[][] dp = new boolean[n][n];for (int i = n - 1; i >= 0; i--) {for (int j = i; j < n; j++) {dp[i][j] = str[i] == str[j] && (j - i <= 2 || dp[i + 1][j - 1]);if (dp[i][j] && (res == null || j - i + 1 > res.length())) {res = s.substring(i, j + 1);}}}return res;
}
时间复杂度: O(n2)O(n^2)O(n2)
空间复杂度: O(n2)O(n^2)O(n2)
n
是字符串长度
从中心扩展
考虑到回文子串是关于中心对称的,因此一个回文子串可以从中心向两边扩展。实际就是暴力搜索的思想,从回文子串的中心向两边展开搜索匹配。另外回文子串的长度可能是奇数也可能是偶数,从而,不仅要以每个字符为中心搜索(此时回文子串长度为奇数),而且还要以相邻字符之间的位置为中心搜索(此时回文子串长度为偶数),因此我们需要搜索 2n−12n-12n−1 个中心。 例如,对于字符串 aba
,中心是字符 b
,对于字符串 abba
,中心是两个 b
之间的位置。
public String longestPalindrome(String s) {if (s == null || s.length() == 0)return s;// 记录当前最长回文子串开始字符和结束字符的位置int start = 0, end = 0;for (int i = 0; i < s.length(); ++i) {// 回文中心是字符int len1 = searchFromCenter(s, i, i);// 回文中心在字符之间int len2 = searchFromCenter(s, i, i + 1);int len = Math.max(len1, len2);// 出现更长的回文子串,更新位置信息if (len > end - start) {// 统一处理成开始字符和结束字符的位置start = i - (len - 1) / 2;end = i + len / 2;}}return s.substring(start, end + 1);
}private int searchFromCenter(String s, int left, int right) {int l = left, r = right;while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {l--;r++;}return r - l - 1;
}
时间复杂度: O(n2)O(n^2)O(n2)
空间复杂度:O(1)O(1)O(1)
n
是字符串长度
马拉车算法
关于马拉车算法,单独记录了一下自己的理解:
理解 Manacher’s Algorithm(马拉车算法)——最长回文子串问题
public String longestPalindrome(String s) {/**字符串处理,例如 S = "babad",处理后 T = "@#b#a#b#a#d#$"要在每个字符之间以及字符串首部和尾部添加'#',T 的长度变为2*s.length()+1,另外又要添加'@'和'$'防止越界,因此最终 T 的长度为2*s.length()+3**/char[] T = new char[2*s.length()+3];T[0]='@'; // 避免越左边界,因为'@'肯定和右边的字符不一样,匹配到'@'循环终止T[1]='#';T[T.length-1]='$'; // 避免越右边界,因为'$'肯定和左边的字符不一样,匹配到'$'循环终止int t = 2; // 在首部添加特殊字符后,从下标 2 开始for(char c : s.toCharArray()){T[t++]=c;T[t++]='#';}// maxLen 记录 T 中最长回文子串的长度,maxCenter为最长回文子串的中心int maxLen=0, maxCenter=0; // center为上一个最长回文串的中心,right为其最右端字符的位置int[] R = new int[T.length];int center = 0, right = 0;// Manacher's algorithmfor (int i = 1; i < R.length - 1; ++i) {//算法核心部分if (i < right)R[i] = Math.min(right - i, R[2 * center - i]);// 从T[i]依次向两边匹配while (T[i + R[i] + 1] == T[i - R[i] - 1])R[i]++;// 更新回文串中心和最右端位置if (i + R[i] > right) {center = i;right = i + R[i];}// 如果出现更长的回文子串,更新 maxLen 和 maxCenterif(maxLen < R[i]){maxLen = R[i];maxCenter = i;} } // 计算最长回文子串在S中的起始位置int index = (maxCenter - 1 - maxLen) / 2;//返回S中的最长回文子串return s.substring(index, index + maxLen);
}
时间复杂度: O(n)O(n)O(n)
空间复杂度: O(n)O(n)O(n)
n
是字符串长度
其他解法:字符串哈希。掌握后再补充
最长回文子序列
LeetCode 链接: 516. 最长回文子序列
题目描述
给定一个字符串s
,找到其中最长的回文子序列。可以假设s
的最大长度为1000
。
示例 1:
输入:
"bbbab"
输出:
4
一个可能的最长回文子序列为 “bbbb”。
示例 2:
输入:
"cbbd"
输出:
2
一个可能的最长回文子序列为 “bb”。
dp 解法
定义状态: dp(i,j)
表示字符串 s
中从位置 i
到 位置 j
的最长子序列长度,其中 i <= j
转移方程:
dp(i,j)={1,当i=jdp(i+1,j−1)+2,当s.charAt(i)==s.charAt(j)max{dp(i+1,j),dp(i,j−1)},其他情况dp(i,j)=\begin{cases} 1, & \text {当i=j} \\ dp(i+1, j-1) + 2, & \text {当}s.charAt(i) == s.charAt(j) \\ max \{ dp(i+1, j), dp(i,j-1) \}, & \text {其他情况} \end{cases} dp(i,j)=⎩⎪⎨⎪⎧1,dp(i+1,j−1)+2,max{dp(i+1,j),dp(i,j−1)},当i=j当s.charAt(i)==s.charAt(j)其他情况
这里有一点需要注意,i = j - 1
时,如果有 s.charAt(i) == s.charAt(j)
,那么这时候应该是 dp(i,j) = 2
,但因为初始化时有
dp(i,j)={1,当i=j0,当i≠jdp(i,j)=\begin{cases} 1, & \text {当i=j} \\ 0, & \text {当} i \neq j \end{cases} dp(i,j)={1,0,当i=j当i=j
所以这时候 dp(i,j) = dp(i+1, j-1) + 2
同样满足转移方程,
public int longestPalindromeSubseq(String s) {if(s==null || s.length()==0)return 0;int n = s.length();char[] str = s.toCharArray();int[][] dp = new int[n][n]; for(int i = 0; i < n; i++){dp[i][i] = 1;}for(int j = 1; j < n; ++j){for(int i = j - 1; i >= 0; --i){if(str[i] == str[j]){if(i == j - 1){dp[i][j] = 2;}else{dp[i][j] = dp[i + 1][j - 1] + 2;}}else{dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);}}}return dp[0][n-1];
}
时间复杂度: O(n2)O(n^2)O(n2)
空间复杂度: O(n2)O(n^2)O(n2)
n
是字符串长度
简省版:
public int longestPalindromeSubseq(String s) {if (s == null || s.length() == 0)return 0;int n = s.length();char[] str = s.toCharArray();int[][] dp = new int[n][n];for (int i = n - 1; i >= 0; --i) {dp[i][i] = 1;for (int j = i + 1; j < n; ++j) {if (str[i] == str[j]) {dp[i][j] = dp[i + 1][j - 1] + 2;} else {dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);}}}return dp[0][n - 1];
}
递归思想
递归思想的解法跟 dp 解法 几乎相同,
- 当
s.charAt(i) == s.charAt(j)
时,因为要找回文子序列,所以s.charAt(i)
和s.charAt(j)
就属于结果的回文子序列中,继续递归s.charAt(i+1)
和s.charAt(j-1)
之间的子串; - 当
s.charAt(i) != s.charAt(j)
时,就递归考虑s.charAt(i+1)
和s.charAt(j)
之间的子串以及s.charAt(i)
和s.charAt(j-1)
之间的子串
其中 i <= j
,否则直接返回 0
;另外防止重复计算,如果 memo[i][j]
已经计算过,直接返回
public int longestPalindromeSubseq(String s) {if (s == null || s.length() == 0)return 0;int n = s.length();int[][] memo = new int[n][n];return helper(s, 0, n - 1, memo);}private int helper(String s, int i, int j, int[][] memo) {if (memo[i][j] != 0) {return memo[i][j];}if (i > j) return 0;if (i == j) return 1;if (s.charAt(i) == s.charAt(j)) {memo[i][j] = helper(s, i + 1, j - 1, memo) + 2;} else {memo[i][j] = Math.max(helper(s, i + 1, j, memo), helper(s, i, j - 1, memo));}return memo[i][j];
}
时间复杂度: O(n2)O(n^2)O(n2)
最好情况下 O(n)O(n)O(n),最坏情况下 O(n2)O(n^2)O(n2)
空间复杂度: O(n2)O(n^2)O(n2)
最长回文子串与最长回文子序列相关推荐
- 数组字符串那些经典算法:最大子序列和,最长递增子序列,最长公共子串,最长公共子序列,字符串编辑距离,最长不重复子串,最长回文子串 (转)...
作者:寒小阳 时间:2013年9月. 出处:http://blog.csdn.net/han_xiaoyang/article/details/11969497. 声明:版权所有,转载请注明出处,谢谢 ...
- 最长回文子串python_最长回文子串(Python)
给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为1000. 示例 1: 输入: "babad" 输出: "bab" 注意: &quo ...
- 字符串最长回文子串_最长回文子串
字符串最长回文子串 Problem statement: 问题陈述: Given a string str, find the longest palindromic substring. A sub ...
- 51NOD 1088 最长回文子串1089 最长回文子串 V2(Manacher算法)
回文串是指aba.abba.cccbccc.aaaa这种左右对称的字符串. 输入一个字符串Str,输出Str里最长回文子串的长度. Input 输入Str(Str的长度 <= 1000(第二题要 ...
- 最长回文子串java_5. 最长回文子串
最长回文子串 难度 中等 给你一个字符串s,找到s中最长的回文子串. 示例 1: 输入:s = "babad" 输出:"bab" 解释:"aba&qu ...
- 【LeetCode成长之路:回文字符串运用题】 回文子串与最长回文子串
⭐️前面的话⭐️ 本篇文章介绍有关回文字符串两道题题解,分别为[647. 回文子串 ]和[ 5. 最长回文子串], 难度均为: 中等 标签: 双指针中心扩散 动态规划,展示语言java.
- 代码随想录补打卡 647 回文子串 516 最长回文子序列
647 回文子串 代码如下 func countSubstrings(s string) int { //dp[i][j]数组的含义是i-j这个范围的元素是否为回文串 dp := make([] ...
- 【代码训练营】day56 | 647. 回文子串 516.最长回文子序列
所用代码 java 回文子串 LeetCode 647 题目链接:回文子串 LeetCode 647 - 中等 思路 dp[i] [j]:[i, j]子串是否是回文子串,是回文就是true 递推公式: ...
- 最长回文子串和最长回文子序列
这两个题目我觉得可以放在一起讨论下: 字长回文子序列 首先是回文子序列的题目,涉及到子序列,一般都是不连续的问题: 那么首推动态规划,并且这个题目只要求返回长度,那么dp数组用来存子问题的最长回文串长 ...
最新文章
- c语言初学 循环 的灵活使用小案例
- centos系统下安装python3以及pip3
- C语言给定数字n阶乘的末尾计算零个数(附完整源码)
- Wannafly 挑战赛27 题解
- rabbitmq延迟队列相关
- 2015-2016 XVI Open Cup, Grand Prix of Bashkortostan, SKB Kontur Cup Stage 2
- OpenShift 4 - 查看关键证书到期日期
- 重建二叉树(C++)
- 【渝粤教育】国家开放大学2019年春季 2633轨道交通信号与通信系统 参考试题
- Kafka 居然还会丢消息?
- 软件开发团队的脓包(1-3)皇帝的新装、口号党、废话迷
- 自己动手——实现台达PLC远程监控数据采集
- 微信外卖点餐系统开发教程
- 怎么把ppt文字大小设置一致_其实,90%的大学生根本不会做PPT
- 009 [转载]天才与鬼才:黑客精英-凯文·米特尼克
- 安装第三方库,出现“error: Microsoft Visual C++ 14.0 is required. Get it with “Microsoft Visual……”解决方案
- STM32CubeIDE使用相关设置经验
- 大数据Python基础学习——练习(二)
- 3dmax2014 uv用法_3DSMAX演示一个硬表面模型UV展开的方法
- 针对场景化痛点,锐捷网络推出极简光 2.X,以太全光网再下一城
热门文章
- 研招网:@2021推免生:关于推免,你必须要知道的三件事
- 答复意见评价python_用Python对用户评论典型意见进行数据挖掘
- select or and
- 小米热点显示android,小米9怎么开启热点?小米9开启个人热点教程
- 安兔兔上html5是什么意思,除了安兔兔跑分,还有这6种办法能证明手机强弱
- 爬取6.6w+豆瓣电影之后的分析故事
- python编有趣的小程序_有趣的python小程序
- mysql case when as_mysql - case when用法
- win7打开网络看不到局域网的其他电脑
- Team Leader你会带团队吗?深刻理解团队合作以及原理