自己在2014年12月面试某公司时被问过该问题。虽然当时,自己写出来了自认为效率比较高的算法,但是没有能按照面试官的要求,进行进一步的优化。后来网上搜索该问题,结果引申了一串其他的知识点,看来还是得深究啊!
解法一:暴力法
这种方法就不用多说了吧。这里我给出暴力法中,最优化的方案。
# coding:utf-8
''' 由于是要求最长的回文串,所以我们可以从最长的子串开始遍历。当找到一个回文串后,就可以直接返回
了。不用再遍历那些短的子串了。
对于字符串s, 假设串长度为N,判断过程如下:
1. 最长的子串就是本身,所以判断s是不是子串。假如不是,进行下一步:
2. 然后判断串长度为N - 1的子串是不是,即:s[:N - 1], s[1:]
3. 依次进行下一轮判断.直到找到个是回文串的子串。
'''
# 判断s的子串:"s[i], s[i+1]... s[j]"是否是回文的
def check(s, i, j):
tmp = s[i:j + 1]
#print tmp
#return tmp == tmp.reverse()
return tmp == tmp[::-1]
def LongestPalindrome(s):
if not s:
return "", 0
lenth = len(s)
diffLen = lenth - 1
#maxPalindrome = ""
while diffLen >= 0:
i = 0
j = diffLen
while j < lenth:
if check(s, i, j):
return s[i:j+1], diffLen + 1
i += 1
j += 1
diffLen -= 1
解法二:中心扩展法O(n2)
这个算法思想其实很简单,就是对给定的字符串S,分别以该字符串S中的每一个字符 c 为中心,向两边扩展,记录下以字符 c 为中心的回文子串的长度。时间复杂度为O(n2),空间复杂度仅为O(1)。
但有一点需要注意的是,回文的情况可能是 a b a,也可能是 a b b a。
1 // 分别向左右扩展,返回扩展后的字符串
2 string expand(string s, int left, int right) {
3 int len = s.size();
4 while (left>=0 && right<len && s[left] == s[right])
5 {
6 left--;
7 right++;
8 }
9 return s.substr(left+1, right-left-1);
10 }
11
12 // 求最长回文子串
13 string longestPalindrome(string s) {
14 int len = s.size();
15 if(len<=1) return s;
16
17 string longest;
18 for (int i=0; i<len-1; i++)
19 {
20 string p1 = expand(s, i, i); // 奇数
21 if (p1.size() > longest.size())
22 longest = p1;
23
24 string p2 = expand(s, i, i+1); // 偶数
25 if (p2.size() > longest.size())
26 longest = p2;
27 }
28 return longest;
29 }
面试时个人能想到就是这两种方法。此外此题还有动态规划法,Manacher法,后缀树方法。其中动态规划法这里不予以介绍,可以参考相关后面列出的参考文献。另外的两节会另外单独介绍!
参考文献:
http://songlee24.github.io/2015/05/12/longest-palindromic-substring/