当前位置:   article > 正文

LeetCode算法题解(动态规划)|LeetCode647. 回文子串、LeetCode516. 最长回文子序列_leetcode 回文子序列

leetcode 回文子序列

一、LeetCode647. 回文子串

题目链接:647. 回文子串
题目描述:

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:

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

示例 2:

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

提示:

  • 1 <= s.length <= 1000
  • s 由小写英文字母组成
算法分析:
定义dp数组及下标含义:

创建一个布尔类型的二维dp数组,dp[i][j]表示下标i到j的字符串是否是回文字符串

递推公式:

如果s[i] != s[j],那么从下标i到j的字符串不是回文字符串,即dp[i][j] = false。

如果s[i] == s[j],那么dp[i][j]可以从三个方向推出来:

1、i == j 时,就一个字符,是回文串。

2、j - i = 1时,就是两个相同字符,例如aa,也是回文串。

3、j - i > 1时,就要判断从下标i+1到下标j-1的字符串是否是回文串了(也就是判断ij两端字符内部的字符串是否是回文串),即dp[i][j]=dp[i+1][j-1]。

初始化:

dp数组每个元素都需先初始化成false。

遍历顺序:

根据递推公式,公式里dp[i][j]可能会由dp[i+1][j-1]推出来,也就是说处理上面之前就需先处理下面,处理左边右边之前先需处理左边,所以i是倒叙遍历,j是正序遍历,而根据dp的定义j是大于等于i的,所以遍历j的时候从j=i开始遍历。

打印dp数组进行验证。

代码如下:

  1. class Solution {
  2. public int countSubstrings(String s) {
  3. int len = s.length();
  4. boolean[][] dp = new boolean[len][len];//字符串i到j是否是回文串
  5. int sum = 0;
  6. for(int i = len - 1; i >= 0; i--) {
  7. char a = s.charAt(i);
  8. for(int j = i; j < len; j++) {
  9. if(s.charAt(j) == a) {//当字符ij相等时
  10. if(j - i <= 1 || dp[i + 1][j - 1]) {//字符串长度再两个字符以内的情况或者内部字符串是回文的情况下,这个字符串都是回文字符串
  11. dp[i][j] = true;
  12. sum++;
  13. }
  14. }
  15. }
  16. }
  17. // for(int i = 0; i < len; i++) {
  18. // for(int j = 0; j < len; j++) {
  19. // System.out.print(dp[i][j] + " ");
  20. // }
  21. // System.out.println();
  22. // }
  23. return sum;
  24. }
  25. }

二、LeetCode516. 最长回文子序列

题目链接:516. 最长回文子序列
题目描述:

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

示例 1:

输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。

示例 2:

输入:s = "cbbd"
输出:2
解释:一个可能的最长回文子序列为 "bb" 。

提示:

  • 1 <= s.length <= 1000
  • s 仅由小写英文字母组成
算法分析:
定义dp数组及下标含义:

dp[i][j]表示从下表i到j的子串中最长回文子串的长度。

递推公式:

如果s[i]==s[j],那么有三种情况:

1、若j-i == 0,即子串只有一个字符,那么最长回文子串的长度为1,dp[i][j]=1;

2、若j-i ==1,即子串只有相等的两个字符,那么最长回文子串的长度为2,dp[i][j]=2;

3、若j-i > 1,那么dp[i][j] = dp[i+1][j-1]+2,即i到j的最长回文子串长度等于其内部(不包含两端)最长回文子串的长度加2。

如果s[i] != s[j] ,那么dp[i][j]可以有两个方向推导出来:

1、删掉左端的i字符串,那么i到j的最长回文子串长度就是i+1到j的最长回文子串长度,即dp[i][j]=dp[i+1][j];

2、删掉右端的j字符,那么i到j的最长回文子串长度就是i到j-1的最长回文子串长度,即dp[i][j]=dp[i][j-1];

初始化:

dp数组所有元素初始化为0。

遍历顺序:

根据递推公式中,dp[i][j]可能会有dp[i+1][j-1]推出来,所以需要先处理大的i和小的j,也即倒叙遍历i,正序遍历j,而根据dp数组的定义i是字符串的左区间,j是右区间,所以j需要大于等于0,也即j从j=i开始向后遍历。

打印dp数组进行验证。

代码如下:

  1. class Solution {
  2. public int longestPalindromeSubseq(String s) {
  3. int len = s.length();
  4. int[][] dp = new int[len][len];//dp[i][j]表示从下表i到j的字符串中最长回文串的长度
  5. for(int i = len - 1; i >= 0; i--) {
  6. char a = s.charAt(i);
  7. for(int j = i; j < len; j++) {
  8. if(s.charAt(j) == a) {//如果ij相等,分三种情况
  9. if(j - i == 0) dp[i][j] = 1;//i到j的字符串长度为1,那么只有一个字符,最长回文串长度为1
  10. else if(j - i == 1) dp[i][j] = 2;//i到j的字符串长度为2,只有两个相等的字符,最长回文长度为2
  11. else dp[i][j] = dp[i+1][j-1] + 2;//i到j的字符串长度大于2,那么i到j的最长回文长度就等于其内部最长回文子串长度加2
  12. }else{//如果不相等,删掉一边的字符,两种情况取最大值
  13. dp[i][j] = Math.max(dp[i+1][j],dp[i][j-1]);
  14. }
  15. }
  16. }
  17. // for(int i = 0; i < len; i++) {
  18. // for(int j = 0; j < len; j++) {
  19. // System.out.print(dp[i][j] + " ");
  20. // }
  21. // System.out.println();
  22. // }
  23. return dp[0][len - 1];
  24. }
  25. }

总结

这两道题都是回文子串问题,不过第一个是求回文子串的个数,第二个是求最长回文子串的长度。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/470774
推荐阅读
相关标签
  

闽ICP备14008679号