📌  相关文章
📜  检查字符串的两半是否都是回文(1)

📅  最后修改于: 2023-12-03 15:10:52.986000             🧑  作者: Mango

检查字符串的两半是否都是回文

在字符串处理中,经常需要判断字符串是否是回文。一种常见的情况是判断一个字符串的两半是否都是回文。本文将介绍如何实现这个功能。

方法一:暴力解法

最简单的方法是直接将字符串分为两半,分别判断两半是否都是回文。这个方法的时间复杂度为 $O(n^2)$,因为对于每一个子串都需要判断是否是回文。

def is_palindrome(s):
    return s == s[::-1]

def is_half_palindrome(s):
    mid = len(s) // 2
    left = s[:mid]
    right = s[-mid:]
    return is_palindrome(left) and is_palindrome(right)

s = 'abcba'
print(is_half_palindrome(s))  # True

这个方法虽然简单,但是时间复杂度比较高,在长字符串上会比较慢。

方法二:优化解法

我们可以采用一种类似于双指针的方法来判断回文。我们从两端同时扫描字符串,如果遇到不同的字符,说明两半不是回文;如果扫描完了整个字符串,说明两半都是回文。

这个方法的时间复杂度为 $O(n)$,是线性级别的,比方法一要快得多。

def is_half_palindrome(s):
    left = 0
    right = len(s) - 1
    while left < right:
        if s[left] != s[right]:
            return False
        left += 1
        right -= 1
    return True

s = 'abcba'
print(is_half_palindrome(s))  # True
方法三:Manacher算法

Manacher算法是一个经典的字符串算法,它可以在线性时间内找出一个字符串中的所有回文子串。虽然Manacher算法的时间复杂度比上面的方法要低,但是它的实现过程比较复杂,不适合小白。

def manacher(s):
    # 对字符串进行预处理,插入一个不在原字符串中的字符(比如#),
    # 这样可以解决奇偶性问题
    t = "#" + "#".join(s) + "#"

    # 记录以每个字符为中心的最长回文子串的长度
    p = [0] * len(t)
    max_len = 0
    center = 0
    for i in range(len(t)):
        if i < max_len:
            # 如果当前位置在已知的最长回文子串内部,可以利用对称性加速处理
            p[i] = min(p[2 * center - i], max_len - i)
        else:
            p[i] = 1
        while i - p[i] >= 0 and i + p[i] < len(t) and t[i - p[i]] == t[i + p[i]]:
            p[i] += 1
        if i + p[i] > max_len:
            max_len = i + p[i]
            center = i

    return [p[i] - 1 for i in range(len(p))]

def is_half_palindrome(s):
    n = len(s)
    if n % 2 == 0:
        # 如果字符串长度是偶数,先判断左右两半是否相等
        if s[:n // 2] != s[n // 2:]:
            return False
    p = manacher(s)
    return all([p[i] >= abs(i - n // 2) for i in range(len(p))])

s = 'abcba'
print(is_half_palindrome(s))  # True

这个方法的时间复杂度为 $O(n)$,比上面的方法还要快一些,但是实现难度比较大。