一. Longest Palindromic Substring
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example:
Input: “babad”
Output: “bab”
Note: “aba” is also a valid answer.
Example:
Input: “cbbd”
Output: “bb”
Difficulty:Medium
TIME:TIMEOUT
解法
求一个字符串是否是是回文的时间复杂度大约为 O(n) ,如果采用最原始的解法,就是分两次遍历字符串得到所有子串,然后判断该子串是否为回文字符串,时间复杂度约为 O(n3) ,如果字符串是1000大小的话,肯定会超时。因此,可以利用回文的一些特性进行减枝,比如回文的两端肯定是一样的字符。这样的话,可以采取这样一种算法,就是先遍历一遍数组,记录下每个字符出现的位置,然后只对两端一样的子串进行回文判断,这样的话时间复杂度会少很多。
bool inline isPalindrome(string s, int left, int right) {
for (int i = left; i <= (right + left) / 2; i++) {
if (s[i] != s[right - i + left]) {
return false;
}
}
return true;
}
string longestPalindrome(string s) {
unordered_map<int, vector<int> > m;
int len = 0;
int r_left = 0;
for (int i = 0; i < s.size(); i++) {
m[s[i]].push_back(i);
}
for (auto i = m.begin(); i != m.end(); i++) {
if ((i->second).size() < 2 && len >= 1)
continue;
for (int j = 0; j < (i->second).size(); j++) {
for (int k = j; k < (i->second).size(); k++) {
int left = (i->second).at(j);
int right = (i->second).at(k);
if (right - left + 1 < len)
continue;
if (left > r_left && right - left + 1 == len) //只取最先出现的同等大小子串
continue;
if (isPalindrome(s, left, right)) {
if (right - left + 1 > len) {
r_left = left;
len = right - left + 1;
}
}
}
}
}
return s.substr(r_left, len);
}
代码的时间复杂度比 O(n2) 大,但应该小于 O(n3) 。
解法二
第一种解法以回文字符串的开始位置作为基础来查找,但是,如果有
k
个子字符串都以该点为起始位置,那么除非遍历完这k个字符串,否则不能确定哪个子字符串是最长的回文串,因此,这种方法会浪费很多的信息。
如果我们以回文串字符串的中间位置为基础,那么以中间作为扩展,在不能扩展的时候就唯一得到了以该中间位置为基础的最长回文串了,那么这样很多信息都没有浪费,时间复杂度会降低很多。(用有用的信息做了有用的事)
string longestPalindrome(string s) {
if (s.size() <= 1)
return s;
int len = 0;
int r_left = 0;
int j, k;
for (int i = 0; i < s.size();i++) {
j = i;
/*重复的字符一定是回文串,这一步虽然不是必须的,但却极大地优化了代码的运行*/
while (i < s.size() - 1 && s[i] == s[i + 1])
i++;
k = i;
/*两边扩展回文串,直到不能扩展为止,得到该中心点最长子串*/
while (j >= 0 && k < s.size() && s[k] == s[j]) {
++k;
--j;
}
if (k - j - 1 > len) { //这里的k比回文串结束位置多一位,j比回文串初始位置少一位
len = k - j - 1;
r_left = j + 1;
}
}
return s.substr(r_left, len);
}
时间复杂度为
总结
这道题给了我一个启示,就是回文串重要的不是起始位置,而是回文串的中心点,通过中心点的扩展,可以得到该中心点唯一最大的回文串。因此,通过遍历中心点求一个字符串的所有子串,复杂度只需要
O(n2)
。
而且这道题也可以有很多变种,比如求指定长度的回文子串,或者是求一个字符串的所有回文子串,或者是求两个字符串中共同的回文子串(可以思考一下)。