在问 · 算法题解

214. 最短回文串

困难字符串KMP 前缀函数力扣原题 ↗

题目描述

输入字符串 s。你只能在 s 的开头添加字符,目标是让整个字符串变成回文串。

在所有能得到的回文串中,返回最短的那一个。

示例 1
输入:s = "aacecaaa"
输出:"aaacecaaa"

解释:在前面补一个 a,得到回文串 "aaacecaaa"。

示例 2
输入:s = "abcd"
输出:"dcbabcd"

解释:要把 "dcb" 补到前面,才能得到回文串。

数据范围

  • 0 <= s.length <= 5 * 10^4
  • s 只含小写英文字母。

解题思路:KMP 前缀函数

关键是找到 s 的最长回文前缀 s[:k],答案就是把剩下的 s[k:] 反转后放到前面。“前缀是回文”等价于“s 的前缀等于 reverse(s) 的后缀”,所以构造 t = s + "#" + reverse(s),用 KMP 的前缀函数 pi 求 t 最长的相等真前后缀,pi[-1] 就是 k。中间的 "#" 保证匹配不会跨过分隔符,使 k 不超过 len(s)。

为什么正确

处理完下标 i 后,pi[i] 是 t[:i+1] 最长的、既是前缀又是后缀的真子串长度。

复杂度分析

时间 O(n),j 的总回退次数不超过总前进次数;空间 O(n)。

易错点

不加分隔符时,像 s="aa" 这样的串会让 pi[-1] 超过 len(s)。暴力逐个判断前缀是否回文是 O(n²),在 "aaa…ab…aaa" 这类用例上会超时。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def shortestPalindrome(self, s: str) -> str:
        t = s + '#' + s[::-1]
        pi = [0] * len(t)
        for i in range(1, len(t)):
            j = pi[i - 1]
            while j > 0 and t[i] != t[j]:
                j = pi[j - 1]
            if t[i] == t[j]:
                j += 1
            pi[i] = j
        k = pi[-1]
        return s[k:][::-1] + s

载入编辑器,自己跑一遍 →

其他解法

两种解法都在求 s 的最长回文前缀。KMP 把它转化为 s + '#' + reverse(s) 的最长相等前后缀,结果确定;哈希逐个前缀比较正反哈希值,写法简单但有冲突风险。

解法 2:字符串哈希 时间 O(n),空间 O(1)(不计答案)

问题等价于找 s 最长的回文前缀。从左到右枚举前缀长度,同时维护两个哈希值:正向哈希 forward = forward·base + c;反向哈希 backward = backward + c·base^i,等价于把当前前缀的反转串按同一方式哈希。两者相等就认为该前缀是回文,记录最长的一个。最后把剩余后缀反转后拼在 s 前面。代码中用两个模数同时计算,降低哈希冲突的概率。

思路直观,只用常数个变量。存在极小的哈希冲突概率,结果不是严格保证正确;取模运算也比 KMP 的比较更慢。

class Solution:
    def shortestPalindrome(self, s: str) -> str:
        base = 131
        mods = (1000000007, 998244353)
        fwd = [0, 0]
        bwd = [0, 0]
        mul = [1, 1]
        best = 0
        for i, ch in enumerate(s):
            c = ord(ch)
            for t in range(2):
                m = mods[t]
                fwd[t] = (fwd[t] * base + c) % m
                bwd[t] = (bwd[t] + c * mul[t]) % m
                mul[t] = mul[t] * base % m
            if fwd == bwd:
                best = i + 1
        return s[best:][::-1] + s

字符串 专题的其他题目

查看 字符串 全部题目 →