5. 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"
Show Company Tags
Show Tags
Show Similar Problems
S1: naive enumeration
以每个字符为轴往左右查找palindromic substring
string longestPalindrome(string s) {
int size = s.size();
if(size < 1) return "";
string substring = s.substr(0,1);
for(int i = 1; i < size; ++i){
if(s[i] == s[i-1]){
int j = 1;
while(i+j < size && i-j > 0 && s[i+j] == s[i-j-1]) j++;
if(2*j > substring.size()) substring = s.substr(i-j, 2*j);
}
if(i - 2 >= 0 && s[i] == s[i-2]){
int j = 1;
while(i+j< size && i-2-j >= 0 && s[i+j] == s[i-2-j]) j++;
if(2*j+1 > substring.size()) substring = s.substr(i-j-1, 2*j+1);
}
}
return substring;
}
S2:enumeration with prunning
如果子串中有许多连续重复字母的话,可以进一步优化。最长回文子串的对称轴一定处在任意个连续的重复字母的中心。不然会导致回文子串短于这些连续字母。
这样可以每次扫描一段连续重复字母的左右位置,作为扩展的起始位置。
string longestPalindrome(string s) {
string result = "";
for (int left = 0, right = left; left < s.size(); left = right) {
right = left + 1;
//跳过重复字符
while (right < s.size() && s[right] == s[right-1]) {
right++;
}
//尝试向两边扩张
int curLeft = left - 1, curRight = right;
while (curLeft >= 0 && curRight < s.size() && s[curLeft] == s[curRight]) {
curLeft--;
curRight++;
}
int len = curRight - curLeft - 1;
if (len > result.size()) {
result = s.substr(curLeft + 1, len);
}
}
return result;
}
S3: DP
isPal[i][j]表示s[j]到s[i]是palindrome
//error注意:没有通过aabaaa的test case, output "maaa"没看出来为何
string longestPalindrome(string& s) {
string result = "";
int n = s.size();
if (n <= 1) {
return s;
}
vector<vector<bool>> isPal(n, vector<bool>(n, false));
isPal[0][0] = true;
for (int i = 1; i < n; ++i) {
isPal[i][i] = true;
isPal[i][i-1] = s[i] == s[i-1];
if (isPal[i][i-1] && result.size() < 2) {
result = s.substr(i-1, 2);
}
for (int j = i - 2; j >= 0; --j) {
isPal[i][j] = (isPal[i-1][j+1] && s[i] == s[j]);
if (isPal[i][j] && j - i + 1 > result.size()) {
result = s.substr(j, i - j + 1);
}
}
}
return result;
}
214. Shortest Palindrome
Given a string S, you are allowed to convert it to a palindrome by adding characters in front of it. Find and return the shortest palindrome you can find by performing this transformation.
For example:
Given "aacecaaa", return "aaacecaaa".
Given "abcd", return "dcbabcd".
S1: naive enumeration
从数组的中间开始,穷举所有以s[i]为中心的palindrome,直到得到结果。注意s[i]取最优解的情况是以s[i+1]为轴而不是以s[i]和s[i+1]之间为轴。
string shortestPalindrome(string s) {
int size = s.size();
for(int mid = size/2-1; mid >= 0; --mid){
int j = 0;
while(mid-j >= 0 && mid+2+j < size && s[mid-j] == s[mid+2+j]) j++;
if(mid-j < 0){
string result = "";
for(int i = size-1; i >= 2*mid+3; --i)
result += s[i];
result += s;
return result;
}
else{
j = 0;
while( mid-j >= 0 && mid+1+j < size && s[mid-j] == s[mid+1+j]) j++;
if(mid-j < 0){
string result = "";
for(int i = size-1; i >= 2*(mid+1); --i)
result += s[i];
result += s;
return result;
}
}
}
string result = "";
for(int i = size-1; i > 0; --i) result += s[i];
result += s;
return result;
}
S2: KMP trick
只用找到从0开始的最长palindrome,再把剩下的字符reverse贴到前面即可。
比如abacd, aba是从0开始的最长palindrome, 只用把cd翻转为dc贴到前边得到dcabacd就是答案。
求从0开始的最长palindrome,trick是构造一个新string l = s + '#' + reverse(s),然后参考KMP算法中构造lps:longest proper prefix which is also suffix 的过程。(这里假设#不在原string中,#是为了在reverse(s)开始的时候将len清零)
string shortestPalindrome(string s) {
string rs(s.rbegin(), s.rend());
string l = s + '#' + rs;
vector<int> table(l.size(), 0);
int len = 0, i = 1, size = s.size();
while( i < l.size()){
if(l[i] == l[len]){
len++;
table[i] = len;
i++;
}
else if(len > 0){
len = table[len-1];//???
}
else{
table[i] = 0;
i++;
}
}
return l.substr(size+1, size-table[l.size()-1])+s;
}
266. Palindrome Permutation
Given a string, determine if a permutation of the string could form a palindrome.
For example,"code"
-> False,"aab"
-> True,"carerac"
-> True.
S: bitset O(n)
只能有最多一个char出现奇数次,即出现在中间
bool canPermutePalindrome(string s) {
bitset<256> count;
for (char c : s) {
count.flip(c);
}
return count.count() < 2;
}
409. Longest Palindrome
Given a string which consists of lowercase or uppercase letters, find the length of the longest palindromes that can be built with those letters.
This is case sensitive, for example"Aa"
is not considered a palindrome here.
S: O(n)
最多只有一个char出现奇数次,其他出现奇数次的char都要减一
int longestPalindrome(string s) {
unordered_map<char, int> count;
for (char c : s) count[c]++;
int len = 0;
bool hasOdd = false;
for (auto i : count) {
if (i.second & 1) {
hasOdd = true;
len += i.second - 1;
} else len += i.second;
}
return hasOdd? len + 1 : len;
}
336. Palindrome Pairs
Given a list ofuniquewords, find all pairs ofdistinctindices(i, j)
in the given list, so that the concatenation of the two words, i.e.words[i] + words[j]
is a palindrome.
Example 1:
Givenwords
=["bat", "tab", "cat"]
Return[[0, 1], [1, 0]]
The palindromes are["battab", "tabbat"]
Example 2:
Givenwords
=["abcd", "dcba", "lls", "s", "sssll"]
Return[[0, 1], [1, 0], [3, 2], [2, 4]]
The palindromes are["dcbaabcd", "abcddcba", "slls", "llssssll"]
S1: brutal force O(n^2)
S2: hash map O(n*k^2) n is the size of word list, k is the average size of each word
关键是如何快速找到匹配的word:
- 反转每个词语存入hash map作为key,value为index
- 将word拆分为 left | right 两部分,只需要找到candidate满足candidate = left且right为palindrome(left|right|candidate) 或者 candidate = right且left为palindrome(candidate|left|right) 注意当中间那部分为空时会出现重复,只计算一次
class Solution {
public:
vector<vector<int>> palindromePairs(vector<string>& words) {
unordered_map<string, int> revMap;
vector<vector<int>> result;
for (int i = 0; i < words.size(); ++i) {
string s = words[i];
reverse(s.begin(), s.end());
revMap[s] = i;
}
for (int i = 0; i < words.size(); ++i) {
int n = words[i].size();
for (int j = 0; j <= n; ++j) {
string left = words[i].substr(0, j);
string right = words[i].substr(j, n - j);
if (revMap.find(left) != revMap.end() && revMap[left] != i &&isPal(right)) { //left|right|candidate
result.push_back(vector<int>{i, revMap[left]});
}
if (left.size() > 0 && revMap.find(right) != revMap.end() && revMap[right] != i
&& isPal(left)) { //candidate|left|right
result.push_back(vector<int>{revMap[right], i});
}
}
}
return result;
}
bool isPal(string s) {
int left = 0, right = s.size() - 1;
while (left < right) {
if (s[left++] != s[right--]) return false;
}
return true;
}
};