Java描述 LeetCode,647. Palindromic Substrings 回文子串

大家好,我是河海哥,专注于后端,如果可以的话,想做一名code designer而不是普通的coder,一起见证河海哥的成长,您的评论与赞是我的最大动力。

1-1:题目

Given a string s, return the number of palindromic substrings in it.

A string is a palindrome when it reads the same backward as forward.

A substring is a contiguous sequence of characters within the string.

Example 1:

Input: s = "abc"
Output: 3
Explanation: Three palindromic strings: "a", "b", "c".

Example 2:

Input: s = "aaa"
Output: 6
Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".

Constraints:

1 <= s.length <= 1000
s consists of lowercase English letters.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindromic-substrings

1-2:idea

1-2-1:自己的想法

我的思路:首先这边是一个字符串,问一共有多少个最先想到的是dp,用一个一维数组dp[i]来表示,s[0:i]的回文子序列的数量,之后再找到状态转移方程就可以解决这个问题,然而在自己列表找状态转移方程的时候,发现这个递推关系很难找,多一个字符,就会多出几种子序列,就得对这些个子序列分别看是不是回文的。于是这个想法宣布gg。

1-2-2:答案-dp解法

之后看了眼答案,答案用的是二维dp数组,布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的⼦串是否是回⽂⼦串,如果是dp[i][j]为true,否则为false。ok,就看了这一行点子就来了。

  • dp状态:布尔类型的dp[i][j]:表示区间范围[i,j] ,这边i>=j哈,这边下面有用(注意是左闭右闭)的⼦串是否是回⽂⼦串,如果是dp[i][j]为true,否则为false
  • 状态转移方程:
    • 是不是回文串,首先s[i]和s[j]得相等吧,不相等肯定是false。默认初始化boolean数组就是全部都是false,也就是啥也不需要干
    • 那么如果相等呢?
      • 相等的时候,比如aba,i=0,j=2,根据正常人的思维,很容易就想到,往里面缩呗,对,就往里面缩,怎么缩?i+1,j-1呀,对!缩了一个之后就变成了一个子问题:s[i+1,j-1]是不是回文啊?如果他是,那两边各加一个a,也一定是啊。所以一个状态转移方程就出来了,if (dp[i + 1][j - 1]) {dp[i][j] = true}
      • 完事了吗?并没有,上面是缩的方法,要是一共就2个字符呢?aa,i=0,j=1,能往里面缩吗?必然不行,这样不就缩没了?这个时候,只要两个相等就到终点了,不用往里面缩了!也就是不依赖于子问题,可以直接写了!dp[i][j] = true
      • 完事了吗?还有一种情况,i和j刚好落在了一个字符上,比如a,这个时候也直接写:dp[i][j] = true,这个情况其实可以和上面的情况合并起来。
  • 确定遍历顺序:我们平时都是初始化前面的,从左到右,从上到下这样遍历,但是这边绝对不是,根据状态转移方程,当前行依赖于下一行,而不是上一行,所以得从下到上,从左到右这样的顺序去遍历。这也是为什么要把遍历顺序放在初始化前面说,不然你咋知道初始化哪里呢?
  • 初始化:默认false就行,因为状态转移方程会把最后一行写好。

1-2-3:dp代码

public static int countSubstrings(String s) {
    int n = s.length();
	
	// 如果你选择boolean[][] dp = new boolean[n+1][n+1]也是可以的,完全不影响,只要代码里面稍微注意一下就可以了
    boolean[][] dp = new boolean[n][n];
    int sum = 0;
    for (int i = n-1; i >= 0; i--) {
        for (int j = i; j < n; j++) {
            if (s.charAt(i) == s.charAt(j)) {
                if (j - i > 1) {
                    if (dp[i + 1][j - 1]) {
                        dp[i][j] = true;
                        sum++;
                    }
                } else {
                    dp[i][j] = true;
                    sum++;
                }
            }
        }
    }

    System.out.println(Arrays.deepToString(dp));
    return sum;
}

String s = "aaa";
打表:
  a      a      a
a[true,  true,  true]
a[false, true,  true]
a[false, false, true]

这边需要注明的就是第二重遍历,为什么从i开始呢?还记得上面说过,i<=j吗?不然这个字符串都不存在,自然不用考虑了!

1-2-4:答案-中心扩展

之前也听说好像有一个东西叫中心扩展,也知道大概是个什么样子。这次看见答案也自己研究了起来。印象中的中心扩展就是从一个点向两边慢慢扩散,比如判断一个字符串是不是回文的,只要找到中心位置,之后再双指针向两边扩展,边扩展边比较,直到尽头就能表示这个串是不是回文的了。这里是问一个串内的回文子串一共多少个,我想着这个简单,相等一次加一就完事了,那么这里怎么确定一共有多少个中心点呢?这个就难了= =||,第一次整的我真想不到。其实,也应该想到,比如判断一个字符串是不是回文的,这个字符串如果是奇数个,中心点就是一个字符,比如aba,中心点就是b,但是如果是偶数呢?比如baab,此时双指针开始位置,应该是l=1,r=2。所以我之后考虑到这个问题,就想到边遍历的时候边考虑到这两种情况。

1-2-5:中心扩展代码:

public static int countSubstrings2(String s) {
    int n = s.length();
    int sum = 0;

    for (int i = 0; i <= n; i++) {
        int l = i;
        int r = l;

        while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
            sum++;
            l--;
            r++;
        }

        l = i;
        r = i + 1;
        while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
            sum++;
            l--;
            r++;
        }
    }
    return sum;
}

从代码行数来看,不如答案的简单哈,但是我觉得是更好理解的。在外层循环的时候,既把它当作奇数的中心点又把它当作偶数的中心点。这样分别考虑,就可以啦!关于这个中心点,我没法说的太清楚,可以看看答案和这里的https://leetcode-cn.com/problems/palindromic-substrings/solution/liang-dao-hui-wen-zi-chuan-de-jie-fa-xiang-jie-zho/,这个人讲的比较清楚= =,其实是我懒得写了,今天时间不够啦!

1-3:总结

今天这道题,我自己是想不到的,但是告诉我用二维数组来做的话,我自己是做出来的,一般列表之后 都容易发现状态转移方程。然后,还认识到了中心扩展这个方法,积累了一些。虽然中心扩展,花了挺久时间才get到里面的精髓,但是总的还是ok的,两种方法写出来再AC,再总结出一篇blog,大概2个半小时,好像也还行?继续加油啊!河海哥,冲!如有错误还请指正,不胜感激,你的赞和评论是我最大的动力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

河海哥yyds

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值