原题:链接
给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
先简单分析一下,本题需要返回最短的回文串,我们一个自然的想法是源字符串中有没有回文字符串呢?如果有的话,就把它单拎出来,然后以其为轴进行y轴的翻转。极端地情况,如果源字符串不存在回文,就像示例2所示,则固定第一个字符a
(很明显,单个字符本身满足回文的特点),将其余的字符串以a
翻过山和大海,成为dcbabcd
。首先使用一种暴力法,即从字符串末尾依次考察其是否为回文串,如果是则将其单拎出来,设源字符串为s
,剩余的字符串设为remain
,在纸上画一画即可验证返回结果就等于:
def shortestPalindrome(s):
n = len(s)
rev = s[::-1]
for i in range(n):
if s[:n-i] == rev[i:]:
return rev[:i] + s
return ''
其实,我们可以尝试着用双指针的思想来处理回文。结合上面的分析,我们需要找到存在的回文串,而那些不可能存在回文的字符则直接将其拼接到结果上。具体做法就是,首先设定一个指向字符串头和尾的指针i
和j
,其中i
表示一个待进一步确定的回文串的上界,即s[:i]
可能还存在回文串,而j
就是一个不断从尾到头迭代的指针。迭代过程中,一旦满足s[i] == s[j]
,我们有理由将指针i
向前移动一位。迭代循环一次之后,将可能存在回文串的字符串递归重复进行,最后即可得到我们的结果。极端地想,如果源字符串本身就是回文串,则遍历结果就是i
遍历完了整个字符串,因此就直接返回该字符串,这也是递归结束的地方,如果源字符串为abcd
,很明显,i
最后指向1,即只有a
存在回文串,并将剩下的字符串进行拼接成dcb+ shortestPalindrome('a') + bcd
。
代码如下:
def shortestPalindrome(s):
n = len(s)
i = 0
for j in range(n-1, -1, -1):
if s[i] == s[j]:
i += 1
# 递归结束
if i == n:
return s
temp = s[i:]
return temp[::-1] + shortestPalindrome(s[:i]) + temp
上面两种解法的时间复杂度都达到了O(n^2),我们试想有没有存在一种线性时间复杂度的算法。
答案是肯定的,只是我们需要捋一捋如何想到使用KMP来解决这个问题。KMP算法是一个有名的字符串匹配算法,常用与在一个文本串S
中找到模式串P
。而理解KMP算法的核心就是要理解公共前后缀和一个部分匹配表(Partial Match Table),其中PMT用于加速下一步对比而保存的索引表,关于KMP的算法,可以参考这个链接,本文程序就基于此思路构造next
数组。
理解了KMP算法,我们接着来思考一下KMP能解决本题的什么问题。从第一个暴力算法我们得知,我们就是不断地迭代寻找满足回文的串,即
if s[:n-i] == rev[i:]:
return rev[:i] + s
如果我们将源字符串和反向的源字符串拼接在一起,成为一个新的字符串new_str
,这就变成了一个查找new_str
最长公共前后缀的问题了。
具体做法是建立一个new_str
的next
表,其表中的值代表着该索引失配下需要跳转的索引值。因此next_str
的next
表的最后一个值k
就表示源字符串的前k+1
个字符串全是回文,然后提取思路和暴力法相同。此外,在构造new_str
的时候需要添加分隔符,即new_str = s + '*' + s[::-1]
,如果没有分隔符’*’,则字符串会发生混淆以至于得不到正确的最长前后缀长度。代码如下:
def shortestPalindrome(self, s: str) -> str:
# KMP
rev = s[::-1]
new_str = s + '*' + rev
def getNext(p):
# KMP得到next数组的程序
n = len(p)
i, j = 0, -1
next = [0] * n
next[0] = -1
while i < n-1:
if j == -1 or p[i] == p[j]:
i += 1
j += 1
next[i] = j
else:
j = next[j]
return next
next = getNext(new_str)
N = len(s)
# rev[: N-(next[-1]+1)] 就和方法一的rev[:i]一个道理
return rev[: N-(next[-1]+1)] + s
最近武汉灾情严重,全国都带有一些恐慌。加油吧!做好防护,加强宣传,祈求早日打赢这场没有硝烟的战争。除夕天还写了这篇博客…奈何不敢出去浪啊~
晚来天欲雪,能饮一杯无?