1. question: 回文子串(中等)
给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。
回文字符串 是正着读和倒过来读一样的字符串。
子字符串 是字符串中的由连续字符组成的一个序列。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/palindromic-substrings
示例 1:
1 2 3
   | 输入:s = "abc" 输出:3 解释:三个回文子串: "a", "b", "c"
   | 
 
示例 2:
1 2 3
   | 输入:s = "aaa" 输出:6 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
   | 
 
提示:
1 2
   | 1 <= s.length <= 1000 s 由小写英文字母组成
   | 
 
2. answers
这道题最开始我自己以接近超时的方式做出来了。动态规划+双指针法判断回文串。
首先以首尾双指针法判断回文串,这没什么可说的,直接做就行。其次是动态规划,定义数组dp[i]表示字符串s[0:i]所能形成的回文串的数量,那么s[0:i+1]和前面有什么关系吗?答案是有的。
- 字符串s[0:i+1]所能形成的回文串一定是s[0:i]和s[i+1],以及s[x:i+1]所形成的回文串总和。
 
- 其中s[0:i]所形成的回文串数量已经有前面得到,s[i+1]就是一个字符,显然回文串数量为1。
 
- 那么s[x:i+1]实际上就是以s[i+1]为结尾的这i个字符串是否是回文串就行。
 
代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
   | public class Solution_0145 {
      public boolean isSubstrings(String s) {                           int start = 0;         int end = s.length() - 1;
          while(start <= end) {             if(s.charAt(start) != s.charAt(end)) return false;
              start++;             end--;         }
          return true;     }
      public int countSubstrings(String s) {
          int[] dp = new int[s.length()];
          dp[0] = 1;
          for (int i = 1; i < s.length(); i++) {
              dp[i] = dp[i-1] + 1;                          for (int j = 0; j < i; j++) {                 if(isSubstrings(s.substring(j, i + 1))) dp[i] += 1;             }
          }
          return dp[s.length() - 1];     }
      public static void main(String[] args) {
          Solution_0145 s0145 = new Solution_0145();
          String s = "aaa";
          System.out.println(s0145.countSubstrings(s));     } }
  | 
 
上面的时间复杂度接近O(n^3),参考题解,可采用动态规划。代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
   | 
 
 
 
 
 
 
 
 
 
 
 
 
 
  public class Solution_0145_02 {
      public int countSubstrings(String s) {
                   boolean[][] dp = new boolean[s.length()][s.length()];         int result = 0;
                   for (int i = 0; i < s.length(); i++) {             for (int j = 0; j < s.length(); j++) {                 if(i == j) {                     dp[i][j] = true;                     result++;                 }             }         }
                            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)) {                     if(j - i <= 1) {                         dp[i][j] = true;                         result++;                     }                                          else {                         dp[i][j] = dp[i+1][j-1];                         if(dp[i][j]) result++;                     }                 }
              }         }
          return result;     }
      public static void main(String[] args) {         System.out.println();
          Solution_0145_02 s0145_02 = new Solution_0145_02();
          String s = "aaa";
          System.out.println(s0145_02.countSubstrings(s));
      } }
 
  | 
 
除了上面的动态规划,其实可以采用中心扩散双指针法。代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
   | 
 
 
 
 
 
 
  public class Solution_0145_03 {
      public boolean isSubstrings(String s) {                           int start = 0;         int end = s.length() - 1;
          while(start <= end) {             if(s.charAt(start) != s.charAt(end)) return false;
              start++;             end--;         }
          return true;     }
      public int countSubstrings(String s) {
          if(s.length() == 1) return 1;
          int subRange = 0;         int result = 0;
          for (int i = 0; i < s.length(); i++) {
                                        subRange = Math.min(i, s.length() - 1 - i);             while(subRange >= 0) {                 if(isSubstrings(s.substring(i-subRange, i+subRange + 1))) result++;                 subRange--;             }
                                        subRange = Math.min(i, s.length() - 1 - (i + 1));             while(subRange >= 0) {                 if(isSubstrings(s.substring(i-subRange, (i+1)+subRange + 1))) result++;                 subRange--;             }         }
          return result;     }
      public static void main(String[] args) {         System.out.println();         Solution_0145_03 s0145 = new Solution_0145_03();
          String s = "ab";
          System.out.println(s0145.countSubstrings(s));     } }
 
  | 
 
3. 备注
参考力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 (leetcode-cn.com),代码随想录 (programmercarl.com)。