回文串问题

回文串问题

647. 回文子串

回文串算是比较经典的算法题了,回文串的概念是比较好理解的,但是解题逻辑比较难想。

我们不能再按照题目问什么,我们就把dp数组设置成什么了。因为我们如果把dp数组设置成回文子串 的数目。则发现,递推公式是没办法写出来的,当前数组和其他数组之间的关系是很难找到的,所以本题重点在于dp数组的定义。

  1. 确定dp数组(dp table)以及下标的含义:

    我们在判断字符串S是否是回文,那么如果我们知道 s[1],s[2],s[3] 这个子串是回文的,那么只需要比较 s[0]和s[4]这两个元素是否相同,如果相同的话,这个字符串s 就是回文串。

    那么此时我们是不是能找到一种递归关系,也就是判断一个子字符串(字符串的下表范围[i,j])是否回文,依赖于,子字符串(下表范围[i + 1, j - 1])) 是否是回文。

    所以我们把dp数组定义为布尔类型,布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。

  2. 递推公式

    • s[i]与s[j]不相等:dp[i][j]一定是false。

    • s[i]与s[j]相等:

      • 下标i 与 j相同,同一个字符例如a,当然是回文子串

      • 下标i 与 j相差为1,例如aa,也是回文子串

      • 下标:i 与 j相差大于1的时候,这个区间是不是回文就看dp[i + 1][j - 1]是否为true。

    if(s.charAt(i) == s.charAt(j)) {
         if(j-i <= 1) {//情况1和情况2
             res++;
             dp[i][j] = true;
         }
         else if(dp[i+1][j-1]) {
             res++;
             dp[i][j] = true;
         }
     }
  3. 初始化:全部初始化为false,或者把对角线全部初始化为true,这样递推的时候就可以少判断一个情况。

  4. 遍历顺序:根据递推公式dp[i+1][j-1]可知:一定要从下到上,从左到右遍历,这样保证dpi + 1都是经过计算的

class Solution {
    public int countSubstrings(String s) {
        boolean[][] dp = new boolean[s.length()][s.length()];//左闭右闭区间
        int res = 0;
        //从下往上,从左到右,不遍历 j<i的情况
        for(int i = s.length()-1; i >= 0; i--) {
            for(int j = i; j < s.length(); j++) {
                if(s.charAt(i) == s.charAt(j)) {
                    if(j-i <= 1) {//情况1和情况2
                        res++;
                        dp[i][j] = true;
                    }
                    else if(dp[i+1][j-1]) {
                        res++;
                        dp[i][j] = true;
                    }
                }
            }
        }
        return res;
    }
}

516. 最长回文子序列

首先,我们要明确本题的子序列和上一题的子串有什么区别:字串是连续的,子序列可以不连续。

上一题和本题的思路是差不多的,我们用动规五部曲分析一下。

  1. dp数组初始化:

    本题需要计算数量,用boolean类型来定义就不太合适了。

    dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]

  2. 递推公式

    • 如果s[i]与s[j]相同,那么dp[i][j] = dp[i + 1][j - 1] + 2;

    • 如果s[i]与s[j]不相同,说明s[i]和s[j]的同时加入 并不能增加[i,j]区间回文子序列的长度,那么分别加入s[i]、s[j]看看哪一个可以组成最长的回文子序列。

      加入s[j]的回文子序列长度为dp[i + 1][j]

      加入s[i]的回文子序列长度为dp[i][j - 1]

      那么dp[i][j]一定是取最大的,即:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);

  3. 初始化

    对角线初始化为1,可以不用考虑 j==i的情况。

  4. 遍历顺序:从下往上,从左往右。

class Solution {
    public int longestPalindromeSubseq(String s) {
        int[][] dp = new int[s.length()][s.length()];
        for (int i = 0; i < s.length(); i++) {
            dp[i][i] = 1;
        }
        for(int i = s.length()-2; i>=0; i--) {
            for(int j = i+1; j < s.length(); j++) {
                if (s.charAt(i) == s.charAt(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][s.length()-1];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值