回文系列算法题

回文系列的也是困扰了我很久,这里就做一个关于这个系列代码题的总结。

段式回文

(困难) “axyza”->(a)(xyz)(a)

验证回文字符串 II

给出串可以删一次,判断删完之后是否回文

1.字符串中回文子串的数量

dp定义:区间范围[i,j] (左闭右闭)的子串是否是回文子串

s = "aaa"
result = 0
dp = [[False] * len(s) for _ in range(len(s))]
for i in range(len(s)-1, -1, -1): #注意遍历顺序 "aaa"———>6
    for j in range(i, len(s)):# 区间范围[i,j] (左闭右闭)的子串是否是回文子串
        if s[i] == s[j]:
            if j - i <= 1:
                result += 1
                dp[i][j] = True # 单个字符或成对字符
            elif dp[i+1][j-1]:
                result += 1
                dp[i][j] = True
print(result) # 6
for line in dp:
    print(dp)
'''
[[True, True, True], [False, True, True], [False, False, True]]
[[True, True, True], [False, True, True], [False, False, True]]
[[True, True, True], [False, True, True], [False, False, True]]
'''

2.字符串中最长回文子序列长度【长度】【长度】

dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]。

s = "bbbab"
def longestPalindromeSubseq(s):
    dp = [[0] * len(s) for _ in range(len(s))]
    for i in range(len(s)):
        dp[i][i] = 1
    for i in range(len(s) - 1, -1, -1):
        for j in range(i + 1, len(s)):
            if s[i] == s[j]:
                dp[i][j] = dp[i + 1][j - 1] + 2
            else:
                dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
    for line in dp:
        print(line)
    return dp[0][-1]
'''
[1, 2, 3, 3, 4]
[0, 1, 2, 2, 3]
[0, 0, 1, 1, 3]
[0, 0, 0, 1, 1]
[0, 0, 0, 0, 1]
'''

3.字符串中的最长回文子串

第一种,普通解法,通俗易懂。

def longestPalindrome(s):
    def extend(start_idx, end_idx, max_start, max_len):
        """
        扩展函数,用于得到向左向右同步扩展后的最长回文子串
        :param start_idx: 向左扩展的起始位置
        :param end_idx: 向右扩展的起始位置
        :param max_start: 当前最长回文子串的左指针
        :param max_len: 当前最大长度
        :return: 当前最长回文子串的起始位置和长度
        """
        while 0 <= start_idx <= end_idx < len(s):   # 子串起止下标合法时
            if s[start_idx] == s[end_idx]:          # 如果新增的两端字符相等
                cur_str = s[start_idx:end_idx]      # 当前子串是回文串
                cur_len = end_idx + 1 - start_idx   # 当前子串长度
                if max_len < cur_len:
                    max_len = cur_len
                    max_start = start_idx
                start_idx -= 1                      # 左指针向左移动一位
                end_idx += 1                        # 右指针向右移动一位
            else:
                break
        return max_start, max_len

    max_start, max_len = 0, 0                       # 初始化最长回文子串开始位置及长度

    for i in range(len(s)):                         # 进行一次遍历
        left = right = i # 判断长度为奇数的回文子串开始和结束位置
        # 从i位置向两边扩展,搜寻可以得到的最大回文子串
        max_start, max_len = extend(left, right, max_start, max_len)

        left, right = i, i+1 # 判断长度为偶数的回文子串开始和结束位置
        # 从i位置向左扩展,从i+1位置向右扩展,搜寻可以得到的最大回文子串
        max_start, max_len = extend(left, right, max_start, max_len)

    return s[max_start:max_start + max_len]

print(longestPalindrome("babad"))

第二种,动态规划。原理可以看这里
d[i][j]: s[i:j]=1 如果s_i:s_j 是回文串

def longestPalindrome(s):
    longest = 0
    len_s = len(s)
    dp = [[0 for _ in range(len_s)] for _ in range(len_s)]
    sublongeststr = ""
    for j in range(0, len_s):
        for i in range(0, j + 1):
            if j - i <= 1:
                if s[i] == s[j]:
                    dp[i][j] = 1
                    if longest < j - i + 1:
                        longest = j - i + 1
                        sublongeststr = s[i:j + 1]
            else:
                if j - i > 1:
                    if s[i] == s[j] and dp[i + 1][j - 1]:
                        dp[i][j] = 1
                        if longest < j - i + 1:
                            longest = j - i + 1
                            sublongeststr = s[i:j + 1]
    for line in dp:
        print(line)
    return sublongeststr

4.分割回文子串

题目描述:
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。

实例:
输入: “aab”
输出:
[
[“aa”,“b”],
[“a”,“a”,“b”]
]
代码参考的这里

def palindromePartitioning(s):
    '''
    分割回文子串
    dp+dfs
    :param s: str
    :return: list[list[str]]
    '''
    final = []
    lens = len(s)
    dp = [[0 for _ in range(0, lens)] for _ in range(lens)]
    for j in range(0, lens):
        for i in range(0, j + 1):
            if j - i <= 1:
                if s[i] == s[j]:
                    dp[i][j] = 1
            if j - i > 1:
                if s[i] == s[j] and dp[i + 1][j - 1] == 1:
                    dp[i][j] = 1
    for line in dp:
        print(line)
    def helper(i, tmp):
        if i == lens:
            final.append(tmp)
        for j in range(i, lens):
            if dp[i][j] == 1:
                print(i, ",", j + 1)
                print(s[i:j + 1])
                helper(j + 1, tmp + [s[i:j + 1]])

    helper(0, [])
    return final


print(palindromePartitioning("google"))
# d[i][j]: s[i:j]=1 如果s_i:s_j 是回文串
'''
  g  o  o  g  l  e
g[1, 0, 0, 1, 0, 0]
o[0, 1, 1, 0, 0, 0]
o[0, 0, 1, 0, 0, 0]
g[0, 0, 0, 1, 0, 0]
l[0, 0, 0, 0, 1, 0]
e[0, 0, 0, 0, 0, 1]

0 , 1 g
1 , 2 o
2 , 3 o
3 , 4 g
4 , 5 l
5 , 6 e
1 , 3 oo
3 , 4 g
4 , 5 l
5 , 6 e
0 , 4 goog
4 , 5 l
5 , 6 e

[['g', 'o', 'o', 'g', 'l', 'e'], ['g', 'oo', 'g', 'l', 'e'], ['goog', 'l', 'e']]
'''

0.验证回文串

def func(s):
    mid = len(s) // 2
    l = mid-1
    r = mid+1
    while l >= 0 and r < len(s):
       if s[l].upper() == s[r].upper():
           l -= 1
           r += 1
       else:
           return False
    return True # 中心扩散法,从中间往外走

print(func("abcba"))
print(func("abcda"))
def func(s):
    left,right = 0,len(s)-1 # 从外往里走
    while left < right:
        if s[left].isalnum() and s[right].isalnum(): # isalnum判断当前字符是字母或数字
            if s[left].upper() == s[right].upper():
                left += 1
                right -= 1
            else:
                return False # upper转大写判断 lower
        elif not s[left].isalnum():
            left += 1
        elif not s[right].isalnum():
            right -= 1
    return True

print(func("abcba"))
print(func("abcda"))
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值