LeetCode之最长回文串(409)、最长回文子串(5)、回文子串(647)、计数二进制子串(696)

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个子串具有相同数量的连续10:“0011”,“01”,“1100”,“10”,“0011” 和 “01”。

请注意,一些重复出现的子串要计算它们出现的次数。

另外,“00110011”不是有效的子串,因为所有的0(和1)没有组合在一起。

示例2:

输入: "10101"
输出: 4
解释: 有4个子串:“10”,“01”,“10”,“01”,它们具有相同数量的连续10

注意:

  • s.length 在1到50,000之间。
  • s 只包含“0”或“1”字符。

题目链接

思路分析

猜你喜欢

转载自blog.csdn.net/weixin_45666566/article/details/112604535