1、最长回文串(409)
题目描述:
【简单】
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 “Aa” 不能当做一个回文字符串。
注意:
假设字符串的长度不会超过 1010。
示例 1:
输入:
"abccccdd"
输出:
7
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
- 有点熟悉呀,哦!之前做过回文数这道题
思路分析:
要求:基于字符串所有字符,构造最长回文串
回文串:正着读和反着读都一样的字符串
\quad \quad 在一个回文串中,只有最多一个字符出现了奇数次,其余的字符都出现偶数次。那么。对于次数为偶数次的字符,那么所有此字符都可以构成回文串的一部分;对于次数为奇数次的字符,那么所有此字符-1都可以构成回文串的一部分;最后,我们任意加一个奇数次字符即可构成最长回文串。因此,思路如下:
- 对字符计数,偶数直接加上,奇数-1加上
- 最后若出现过奇数次的字符,在加个1(回文中间的字符)
class Solution:
def longestPalindrome(self, s: str) -> int:
odd=0 #标记是否出现过奇数次数的字符,0记没有出现过
res=0
counts={}#统计字符次数
for i in s:
counts[i]=counts.get(i,0)+1
for key in counts.keys():
if counts[key]%2==0:
res+=counts[key]
else:
res+=counts[key]-1
odd=max(odd,counts[key])
return res if odd==0 else res+1
- 时间复杂度: O ( N ) O(N) O(N)
- 空间复杂度: O ( S ) O(S) O(S),其中 S为字符集不同字符数。
2、最长回文子串(5)
题目描述:
【中等】
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
题解一:暴力法
- 列举所有的子串,判断是否为回文串,保存最长的回文串。
class Solution:
#判断字符串是否为回文字符串
def validate_palindrome(self,s:str,left:int,right:int) -> bool:
if len(s)<right and left<0:
assert("s length must greater than right,left must greater than or equal to zero")
while left < right:
if s[left]!=s[right]:
return False
left+=1
right-=1
return True
def longestPalindrome(self, s: str) -> str:
n=len(s)
if n<2:
return s
#用来记录回文字符串
palindrome_s=s[0]
#记录回文字符串长度
max_len=1
for i in range(n-1):
for j in range(i+1,n):
if j-i+1>max_len and self.validate_palindrome(s,i,j):
palindrome_s=s[i:j+1]
max_len=j-i+1
return palindrome_s
-
时间复杂度: O ( n ³ ) O(n³) O(n³)两层 for 循环 O(n²),for 循环里边判断是否为回文 O(n),所以时间复杂度为 O(n³)。
-
空间复杂度:O(1)常数个变量。
-
可以通过,但是超过时间限制,舍弃
题解二:动态规划
-
状态:
dp[i][j] 表示子串 s[i…j] 是否为回文子串,这里子串 s[i…j] 定义为左闭右闭区间,可以取到 s[i] 和 s[j]。 -
状态转移方程:
dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1] -
初始化:
初始化的时候,单个字符一定是回文串,
因此当字符串的长度小于2时,肯定为回文串,且把对角线先初始化为 true,即 dp[i][i] = true 。 -
输出:
只要一得到 dp[i][j] = true,就记录子串的长度和起始位置,没有必要截取,这是因为截取字符串也要消耗性能,记录此时的回文子串的「起始位置」和「回文长度」即可。
注意事项:总是先得到小子串的回文判定,然后大子串才能参考小子串的判断结果,即填表顺序很重要。
class Solution:
def longestPalindrome(self, s: str) -> str:
n=len(s)
#考虑特殊情况:如果字符串的长度为1就是回文字符串
if n<2:
return s
#创建一个二维列表用来保存每个字符串是否为回文字符串
is_p= [[False] * n for _ in range(n)]
#对角线上都是回文字符串,因为都是单个字符
for i in range(n):
is_p[i][i]=True
#用来记录回文字符串的起始位置
s_index=0
max_len=1
#遍历表格,计算字符串是否为回文字符串
for j in range(1,n):
for i in range(0,j):
if s[i]==s[j]:
if j-i<3:
is_p[i][j]=True
else:
is_p[i][j]=is_p[i+1][j-1]
else:
is_p[i][j]=False
#记录最长的回文字符串
current_len=j-i+1
if is_p[i][j] and current_len>max_len:
max_len=current_len
s_index=i
return s[s_index:(s_index+max_len)]
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度: O ( n 2 ) O(n^2) O(n2)
题解三:中心扩散算法
-
遍历每一个索引,以这个索引为中心,利用“回文串”中心对称的特点,往两边扩散,看最多能扩散多远。
-
回文串在长度为奇数和偶数的时候,“回文中心”的形式是不一样的。
- 奇数回文串的“中心”是一个具体的字符,例如:回文串 “aba” 的中心是字符 “b”;
- 偶数回文串的“中心”是位于中间的两个字符的“空隙”,例如:回文串串 “abba” 的中心是两个 “b” 中间的那个“空隙”。
class Solution:
def center_spread(self,s,size,left,right):
"""中心扩散寻找回文字符串
:param s: 字符串
:param size: 字符串的长度
:param left: 开始寻找左边的位置
:param right: 开始寻找右边的位置
:return: 回文字符串,回文字符串的长度
"""
i=left
j=right
#保证在寻找的过程中不发生越界,而且左右两个字符要相等
while i>=0 and j<size and s[i]==s[j]:
i-=1
j+=1
return s[i+1:j],j-i+1
def longestPalindrome(self, s: str) -> str:
size=len(s)
if size<2:
return s
s_palindrome=s[0]
max_len=1
for i in range(size):
#当回文字符串数是奇数时
odd_palindrome,odd_len=self.center_spread(s,size,i,i)
#当回文字符串是偶数的时候
even_palindrom,even_len = self.center_spread(s,size,i,i+1)
#获取最长的回文字符串
cur_palindrome = odd_palindrome if odd_len > even_len else even_palindrom
#更新最长的回文字符串
if len(cur_palindrome) > max_len:
s_palindrome = cur_palindrome
max_len = len(cur_palindrome)
return s_palindrome
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度: O ( 1 ) O(1) O(1)
3、回文子串(647)
题目描述:
【中等】
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:"abc"
输出:3
解释:三个回文子串: "a", "b", "c"
示例 2:
输入:"aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
思路分析:
4、计数二进制子串(696)
题目描述:
【简单】
给定一个字符串 s,计算具有相同数量0和1的非空(连续)子字符串的数量,并且这些子字符串中的所有0和所有1都是组合在一起的。
重复出现的子串要计算它们出现的次数。
示例1:
输入: "00110011"
输出: 6
解释: 有6个子串具有相同数量的连续1和0:“0011”,“01”,“1100”,“10”,“0011” 和 “01”。
请注意,一些重复出现的子串要计算它们出现的次数。
另外,“00110011”不是有效的子串,因为所有的0(和1)没有组合在一起。
示例2:
输入: "10101"
输出: 4
解释: 有4个子串:“10”,“01”,“10”,“01”,它们具有相同数量的连续1和0。
注意:
- s.length 在1到50,000之间。
- s 只包含“0”或“1”字符。
思路分析: