题目

最长回文字符串是一种对称的字符串,如 s = "ababd",其中"aba"或"bab"都是回文字符串。

求解思路

最开始的思路是用类似括号匹配的放手,利用栈来做“对对消”,来判断一个字符串是不是回文字符串,但实际操作中发现 “对称轴” 元素是不确定的,前面的消除会导致后面的无法对比。
然后又被提醒到,回文对称有两种,奇数对称,如"abcba", 和偶数对称,如"abccba"。这种方式也是不一样的。

因此解决思路如下:

  1. 确定一个基础点作为轴点,分别按奇数策略和偶数策略(作为左边点),看是否存在回文字符串,如果两种策略都存在则返回较长的那个,如"bccca", 存在"cc"和"ccc"两种,返回"ccc“。
  2. 遍历这个字符串,依次作为轴点,寻找出所有的回文字符串,组成一个列表
  3. 遍历结果列表,返回最长的那个

代码实现

寻找以一个索引作为轴的回文串-偶数模式,如"abba"

from typing import Optional, List

def get_palindromic_substring_from_index_1(s: str, mid_index: int) -> Optional[str]:
    """寻找字符串s中以mid_index作为轴的对称字符串-偶数模式-作为左边点"""
    if mid_index < 1:
        return None
    # 最大偏移量-取左右两边长度的最小值
    max_offset = min([mid_index, len(s) - mid_index])

    offset = 1
    while offset < max_offset:
        # 偶数模式,左侧偏移量-1
        left, right = s[mid_index - (offset - 1)], s[mid_index + offset]
        if left != right:
            break  # 遇到一个不对称值则跳出循环
        offset += 1
    # offset大于初始值则存在以该点为轴的回文字符串
    if offset > 1:
        offset -= 1
        return s[mid_index - (offset - 1):mid_index + offset + 1]
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

寻找以一个索引作为轴的回文串-奇数模式,如"abcba"

def get_palindromic_substring_from_index_2(s: str, mid_index: int) -> Optional[str]:
    """寻找字符串s中以mid_index作为轴的对称字符串-奇数模式"""
    # 奇数模式
    if mid_index < 1:
        return None
    # 最大偏移量-取左右两边长度的最小值
    max_offset = min([mid_index, len(s) - mid_index])

    offset = 1
    while offset < max_offset:
        left, right = s[mid_index - offset], s[mid_index + offset]
        if left != right:
            break  # 遇到一个不对称值则跳出循环
        offset += 1
    # offset大于初始值则存在以该点为轴的回文字符串
    if offset > 1:
        offset -= 1
        return s[mid_index - offset:mid_index + offset + 1]
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

综合两种模式,寻找以一个索引作为轴的较长的回文串

def get_palindromic_substring_from_index(s: str, mid_index: int) -> Optional[str]:
    """寻找字符串s中以mid_index作为轴的对称字符-综合两种策略返回比较长的的那个"""
    r1 = get_palindromic_substring_from_index_1(s, mid_index)
    r2 = get_palindromic_substring_from_index_2(s, mid_index)
    if r1 is None:
        return r2
    if r2 is None:
        return r1
    # 如果两种模式都存在返回比较长的那个
    if len(r1) > len(r2):
        return r1
    return r2
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

寻找字符串中的所有回文串

def get_all_palindromic_substrings(s: str) -> List[str]:
    """查找所有回文子字符串"""
    results = []
    for mid_index in range(1, len(s)-1):
        r = get_palindromic_substring_from_index(s, mid_index)
        if r is not None:
            results.append(r)
    return results
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

从所有回文串中寻找最长的那个回文串

def get_longest_palindromic_substring(s: str) -> Optional[str]:
    """查找最长回文字符串"""
    results = get_all_palindromic_substrings(s)
    if len(results) == 0:
        return None
    # 在所有回文字符串中寻找最长的一个,如果存在长度相等的,只取其中一个
    return max(results, key=len)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

测试代码

if __name__ == '__main__':
    print(get_longest_palindromic_substring('ababd'))
    print(get_longest_palindromic_substring('aaabacccababa'))
    print(get_longest_palindromic_substring('aaabaccababa'))
  • 1.
  • 2.
  • 3.
  • 4.

注:该算法的时间复杂度为O(n^2)左右,不是最优的算法

点击查看完整代码

from typing import Optional, List


def get_palindromic_substring_from_index_1(s: str, mid_index: int) -> Optional[str]:
    """寻找字符串s中以mid_index作为轴的对称字符串-偶数模式-作为左边点"""
    if mid_index < 1:
        return None
    # 最大偏移量-取左右两边长度的最小值
    max_offset = min([mid_index, len(s) - mid_index])

    offset = 1
    while offset < max_offset:
        # 偶数模式,左侧偏移量-1
        left, right = s[mid_index - (offset - 1)], s[mid_index + offset]
        if left != right:
            break  # 遇到一个不对称值则跳出循环
        offset += 1
    # offset大于初始值则存在以该点为轴的回文字符串
    if offset > 1:
        offset -= 1
        return s[mid_index - (offset - 1):mid_index + offset + 1]


def get_palindromic_substring_from_index_2(s: str, mid_index: int) -> Optional[str]:
    """寻找字符串s中以mid_index作为轴的对称字符串-奇数模式"""
    # 奇数模式
    if mid_index < 1:
        return None
    # 最大偏移量-取左右两边长度的最小值
    max_offset = min([mid_index, len(s) - mid_index])

    offset = 1
    while offset < max_offset:
        left, right = s[mid_index - offset], s[mid_index + offset]
        if left != right:
            break  # 遇到一个不对称值则跳出循环
        offset += 1
    # offset大于初始值则存在以该点为轴的回文字符串
    if offset > 1:
        offset -= 1
        return s[mid_index - offset:mid_index + offset + 1]


def get_palindromic_substring_from_index(s: str, mid_index: int) -> Optional[str]:
    """寻找字符串s中以mid_index作为轴的对称字符-综合两种策略返回比较长的的那个"""
    r1 = get_palindromic_substring_from_index_1(s, mid_index)
    r2 = get_palindromic_substring_from_index_2(s, mid_index)
    if r1 is None:
        return r2
    if r2 is None:
        return r1
    # 如果两种模式都存在返回比较长的那个
    if len(r1) > len(r2):
        return r1
    return r2


def get_all_palindromic_substrings(s: str) -> List[str]:
    """查找所有回文子字符串"""
    results = []
    for mid_index in range(1, len(s)-1):
        r = get_palindromic_substring_from_index(s, mid_index)
        if r is not None:
            results.append(r)
    return results


def get_longest_palindromic_substring(s: str) -> Optional[str]:
    """查找最长回文字符串"""
    results = get_all_palindromic_substrings(s)
    if len(results) == 0:
        return None
    # 在所有回文字符串中寻找最长的一个,如果存在长度相等的,只取其中一个
    return max(results, key=len)


if __name__ == '__main__':
    print(get_longest_palindromic_substring('ababd'))
    print(get_longest_palindromic_substring('aaabacccababa'))
    print(get_longest_palindromic_substring('aaabaccababa'))
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.