2021/5/24回文子串与中心扩展法动态规划法
Posted 黑黑白白君
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021/5/24回文子串与中心扩展法动态规划法相关的知识,希望对你有一定的参考价值。
文章目录
回文子串
【题目】
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
- 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
- 输入:“abc”
- 输出:3
- 解释:三个回文子串: “a”, “b”, “c”
示例 2:
- 输入:“aaa”
- 输出:6
- 解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”
提示:
- 输入的字符串长度不会超过 1000 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindromic-substrings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
【中心拓展法】
计算有多少个回文子串的最朴素方法就是枚举出所有的回文子串,而枚举出所有的回文字串又有两种思路,分别是:
- 枚举出所有的子串,然后再判断这些子串是否是回文。时间复杂度是O(n^3)。
- 枚举每一个可能的回文中心,然后用两个指针分别向左右两边拓展,当两个指针指向的元素相同的时候就拓展,否则停止拓展。时间复杂度是O(n^2)。
- 所以选择第二种方法来枚举所有的回文子串。
如何有序地枚举所有可能的回文中心?
需要考虑回文长度是奇数和回文长度是偶数的两种情况:
- 如果回文长度是奇数,那么回文中心是一个字符。
- 如果回文长度是偶数,那么中心是两个字符。
借鉴该思想写出的代码:
class Solution:
def countSubstrings(self, s: str) -> int:
s=list(s)
n=len(s)
i=0
res=0
while(i<n):
j=i
k=i
while(k>=0 and j<n):
if s[k]==s[j]:
res+=1
k-=1
j+=1
else:
break
j=i+1
k=i
while(k>=0 and j<n):
if s[k]==s[j]:
res+=1
k-=1
j+=1
else:
break
i+=1
return res
# 执行用时:100 ms, 在所有 Python3 提交中击败了96.27%的用户
# 内存消耗:15 MB, 在所有 Python3 提交中击败了55.83%的用户
也可以用一个循环搞定。
- 长度为 n 的字符串会生成 2n-1 组回文中心。
- 其中左中心为 i / 2,右中心为 i / 2+(i mod 2)。
所以只要从 0 到 2n - 2 遍历 i,就可以得到所有可能的回文中心,这样就把奇数长度和偶数长度两种情况统一起来。
借鉴该思路写出的代码:
class Solution:
def countSubstrings(self, s: str) -> int:
s=list(s)
n=len(s)
i=0
res=0
while(i<2*n-1):
j=i//2
k=i//2+i%2
while(j>=0 and k<n and s[k]==s[j]):
res+=1
j-=1
k+=1
i+=1
return res
# 执行用时:132 ms, 在所有 Python3 提交中击败了78.89%的用户
# 内存消耗:14.9 MB, 在所有 Python3 提交中击败了74.30%的用户
【动态规划法】
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,即单个字符,则为True。
- 如果 i 与 j 相差等于1,即两个字符,则为True。
- 如果 i 与 j 相差大于1,而dp[i+1][j-1]是,则dp[i][j]也是,否则为False。
3、初始化时均为False。
4、res统计个数。
5、确认遍历顺序!:
- 首先从递推公式中可以看出,情况三是根据dp[i + 1][j - 1]是否为true,在对dp[i][j]进行赋值true的。
- dp[i + 1][j - 1] 在 dp[i][j]的左下角。所以一定要从下到上,从左到右遍历,这样保证dp[i + 1][j - 1]都是经过计算的。
注意:因为dp[i][j]的定义,所以j一定是大于等于i的,那么在填充dp[i][j]的时候一定是只填充右上半部分。
借鉴该思路写出的代码:
class Solution:
def countSubstrings(self, s: str) -> int:
s=list(s)
n=len(s)
res=0
dp=[[False]*n for i in range(n)]
i=n-1
while(i>=0):
j=i
while(j<n):
if s[i]==s[j]:
if j-i<=1:
dp[i][j]=True
res+=1
elif dp[i+1][j-1]==True:
dp[i][j]=True
res+=1
j+=1
i-=1
return res
# 执行用时:240 ms, 在所有 Python3 提交中击败了53.38%的用户
# 内存消耗:22.4 MB, 在所有 Python3 提交中击败了29.43%的用户
避坑之python初始化二维数组:
- 初始化一个二维数组时,如果以方式multi = [[0] * 5] * 3,其实是不对的。因为[0] * 5是一个一维数组的对象,* 3的话只是把对象的引用复制了3次,此时修改了multi[0][0],却把multi[1][0],multi[2][0]也修改了!!!
- 正确的方式应该是multilist = [[0] * 5 for row in range(3)],或者multilist = [[0 for col in range(5)] for row in range(3)]。
【部分内容参考自】
- 回文子串:https://leetcode-cn.com/problems/palindromic-substrings/solution/hui-wen-zi-chuan-by-leetcode-solution/
- 「代码随想录」647. 回文子串:【动态规划】【中心扩展】详解!:https://leetcode-cn.com/problems/palindromic-substrings/solution/647-hui-wen-zi-chuan-dong-tai-gui-hua-zh-6f7n/
- python中初始化二维数组:https://blog.csdn.net/justheretobe/article/details/7632487
以上是关于2021/5/24回文子串与中心扩展法动态规划法的主要内容,如果未能解决你的问题,请参考以下文章
[LeetCode] 647. 回文子串 ☆☆☆(最长子串动态规划中心扩展算法)