文章目录

  • 最长回文子串与最长回文子序列
    • 最长回文子串
      • 题目描述
      • 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​,true​j−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)

最长回文子串与最长回文子序列相关推荐

  1. 数组字符串那些经典算法:最大子序列和,最长递增子序列,最长公共子串,最长公共子序列,字符串编辑距离,最长不重复子串,最长回文子串 (转)...

    作者:寒小阳 时间:2013年9月. 出处:http://blog.csdn.net/han_xiaoyang/article/details/11969497. 声明:版权所有,转载请注明出处,谢谢 ...

  2. 最长回文子串python_最长回文子串(Python)

    给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为1000. 示例 1: 输入: "babad" 输出: "bab" 注意: &quo ...

  3. 字符串最长回文子串_最长回文子串

    字符串最长回文子串 Problem statement: 问题陈述: Given a string str, find the longest palindromic substring. A sub ...

  4. 51NOD 1088 最长回文子串1089 最长回文子串 V2(Manacher算法)

    回文串是指aba.abba.cccbccc.aaaa这种左右对称的字符串. 输入一个字符串Str,输出Str里最长回文子串的长度. Input 输入Str(Str的长度 <= 1000(第二题要 ...

  5. 最长回文子串java_5. 最长回文子串

    最长回文子串 难度 中等 给你一个字符串s,找到s中最长的回文子串. 示例 1: 输入:s = "babad" 输出:"bab" 解释:"aba&qu ...

  6. 【LeetCode成长之路:回文字符串运用题】 回文子串与最长回文子串

    ⭐️前面的话⭐️ 本篇文章介绍有关回文字符串两道题题解,分别为[647. 回文子串 ]和[ 5. 最长回文子串], 难度均为: 中等 标签: 双指针中心扩散 动态规划,展示语言java.

  7. 代码随想录补打卡 647 回文子串 516 最长回文子序列

    647 回文子串 代码如下 func countSubstrings(s string) int {    //dp[i][j]数组的含义是i-j这个范围的元素是否为回文串 dp := make([] ...

  8. 【代码训练营】day56 | 647. 回文子串 516.最长回文子序列

    所用代码 java 回文子串 LeetCode 647 题目链接:回文子串 LeetCode 647 - 中等 思路 dp[i] [j]:[i, j]子串是否是回文子串,是回文就是true 递推公式: ...

  9. 最长回文子串和最长回文子序列

    这两个题目我觉得可以放在一起讨论下: 字长回文子序列 首先是回文子序列的题目,涉及到子序列,一般都是不连续的问题: 那么首推动态规划,并且这个题目只要求返回长度,那么dp数组用来存子问题的最长回文串长 ...

最新文章

  1. c语言初学 循环 的灵活使用小案例
  2. centos系统下安装python3以及pip3
  3. C语言给定数字n阶乘的末尾计算零个数(附完整源码)
  4. Wannafly 挑战赛27 题解
  5. rabbitmq延迟队列相关
  6. 2015-2016 XVI Open Cup, Grand Prix of Bashkortostan, SKB Kontur Cup Stage 2
  7. OpenShift 4 - 查看关键证书到期日期
  8. 重建二叉树(C++)
  9. 【渝粤教育】国家开放大学2019年春季 2633轨道交通信号与通信系统 参考试题
  10. Kafka 居然还会丢消息?
  11. 软件开发团队的脓包(1-3)皇帝的新装、口号党、废话迷
  12. 自己动手——实现台达PLC远程监控数据采集
  13. 微信外卖点餐系统开发教程
  14. 怎么把ppt文字大小设置一致_其实,90%的大学生根本不会做PPT
  15. 009 [转载]天才与鬼才:黑客精英-凯文·米特尼克
  16. 安装第三方库,出现“error: Microsoft Visual C++ 14.0 is required. Get it with “Microsoft Visual……”解决方案
  17. STM32CubeIDE使用相关设置经验
  18. 大数据Python基础学习——练习(二)
  19. 3dmax2014 uv用法_3DSMAX演示一个硬表面模型UV展开的方法
  20. 针对场景化痛点,锐捷网络推出极简光 2.X,以太全光网再下一城

热门文章

  1. 研招网:@2021推免生:关于推免,你必须要知道的三件事
  2. 答复意见评价python_用Python对用户评论典型意见进行数据挖掘
  3. select or and
  4. 小米热点显示android,小米9怎么开启热点?小米9开启个人热点教程
  5. 安兔兔上html5是什么意思,除了安兔兔跑分,还有这6种办法能证明手机强弱
  6. 爬取6.6w+豆瓣电影之后的分析故事
  7. python编有趣的小程序_有趣的python小程序
  8. mysql case when as_mysql - case when用法
  9. win7打开网络看不到局域网的其他电脑
  10. Team Leader你会带团队吗?深刻理解团队合作以及原理