前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【DP、双指针】647. Palindromic Substrings

【DP、双指针】647. Palindromic Substrings

作者头像
echobingo
发布2019-06-14 11:17:32
6100
发布2019-06-14 11:17:32
举报
题目描述:

Given a string, your task is to count how many palindromic substrings in this string.

The substrings with different start indexes or end indexes are counted as different substrings even they consist of same characters.

Example 1:
代码语言:javascript
复制
Input: "abc"
Output: 3
Explanation: Three palindromic strings: "a", "b", "c".
Example 2:
代码语言:javascript
复制
Input: "aaa"
Output: 6
Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".

Note:

  • The input string length won't exceed 1000.
解题思路:

该题计算字符串中有多少个不同的回文子串,注意:具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。

首先想到暴力,时间复杂度 O(N^3),pass。

方法1(双指针):

回文串按照长度分为两种:长度为奇数的的回文串(如 'a', 'aba')和长度为偶数的回文串(如 'aa', 'abba')。双指针的做法利用了这个性质,将字符串的每个位置 i 分为两种情况:

  • 第一种是长度为 2 的子串(i, i+1)开始,如果这两字符相等,则向两边拓展,判断长度为 4、6... 是不是回文串;
  • 第二种是长度为 3 的子串 (i, i+2) 开始,如果第一个和第三个字符相等,则向两边拓展,判断长度为 5、7... 是不是回文串。

对于每个位置 i,累加两种情况下的回文子串个数,最后,再加上字符串的长度就是答案(因为单个字符也是回文串)。

举例: 如 s = "aaaaa", 假设现在遍历到 s[1] = 'a',分两种情况:

  • 取 [1,2] 构成 'aa',s[1] == s[2],是回文串,则向两端移动;s[0] == s[3],是回文串,则向两端移动;越界,则 s[1] 位置的偶数长度的子串为 2;
  • 取 [1,3] 构成 'aaa',s[1] == s[3],是回文串,则向两端移动;s[0] == s[4],是回文串,则向两端移动;越界,则 s[1] 位置的奇数长度的子串为 2。

每个位置都从长度为 2 和长度为 3 的子串分别计算,向两端扩展,计算偶回文串和奇回文串的个数。

时间复杂度 O(N^2),空间复杂度 O(1)。

Python3 实现:
代码语言:javascript
复制
class Solution:
    # 方法1:双指针,分奇回文串和偶回文串
    def countSubstrings(self, s: str) -> int:
        lens = len(s)
        even = odd = 0
        for i in range(lens):
            even += self.count(s, lens, i, i+1)  # 统计偶回文串个数
            odd += self.count(s, lens, i, i+2)  # 统计奇回文串个数
        return lens + even + odd  # 单个字符也是回文串

    def count(self, s, lens, i, j):
        cnt = 0
        while i >= 0 and j < lens and s[i] == s[j]:
            cnt += 1
            i -= 1
            j += 1
        return cnt

print(Solution().countSubstrings("aaa"))  # 6
print(Solution().countSubstrings("aabba"))  # 8
方法2(动态规划):

动态规划的思想是,dp[i][j] 表示子串 s[i,j] 是不是回文串,初始化为 False。最后,对 dp[lens(s)][len(s)] 所有的 True 求和,就是最后的答案。

接下来找状态转移方程:

当我们要确定 dp[i][j] 是否为 True,需要考虑:(1)dp[i+1][j-1] 是回文串吗?;(2)s[i] == s[j] 吗?

因此,我们得到转移方程:dp[i][j] = True, if dp[i+1][j-1] == True and s[i] == s[j]

举例: 如 s = "abcba", dp[0][4] = True,因为 dp[0+1][4-1] 为 True 且 s[0] == s[4]。

编程时注意点: 1、在进行双循环遍历时,应该按照 (0,0);(1,1)、(0,1);(2,2)、(1,2)、(0,2);(3,3)、(2,3)、(1,3)、(0,3) 的顺序遍历,这样可以保证在计算 dp[i][j] 时 dp[i+1][j-1] 已经计算出来了。 2、对于 s = 'aa' 这种,计算 dp[0][1] 为 True 时,由于 dp[0+1][1-1] 导致 dp[1][0] 无意义,因此对于这种情况,只需要保证 s[0] == s[1] 即可。

Python3 实现:
代码语言:javascript
复制
class Solution:
    # 方法2:dp[i][j] = True, if dp[i+1][j-1] == True and s[i] == s[j]
    def countSubstrings(self, s: str) -> int:
        lens = len(s)
        dp = [[False] * lens for _ in range(lens)]
        for j in range(lens):  # 注意遍历的顺序
            dp[j][j] = True
            for i in range(j-1, -1, -1):   # 注意遍历的顺序
                if i+1 <= j-1 and dp[i+1][j-1] == True and s[i] == s[j]:  
                    dp[i][j] = True
                elif i+1 > j-1 and s[i] == s[j]:
                    dp[i][j] = True
        cnt = 0
        for i in range(lens):
            cnt += sum(dp[i])
        return cnt

print(Solution().countSubstrings("aaaa"))  # 10
print(Solution().countSubstrings("aabbaa"))  # 11
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.06.06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目描述:
    • Example 1:
      • Example 2:
      • 解题思路:
        • 方法1(双指针):
          • Python3 实现:
            • 方法2(动态规划):
              • Python3 实现:
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档