1. 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 长度最长为1000。
示例:
输入: "babad" 输出: "bab" 注意: "aba"也是有效答案
示例:
输入: "cbbd" 输出: "bb"
三种解题思路,第一种遍历所有字串,暴力法,复杂度o(n3),这里就不讲了;第二种,观察发现回文都是对称,由中心向两边扩展,可以遍历每个元素,计算最大的扩展宽度,当然需要考虑,偶数和奇数回文,复杂度o(n2);第三种,是在第二种上进行优化,也就是Manacher算法,复杂度O(n),具体算法分析请看https://www.felix021.com/blog/read.php?2040。下面给出后两种代码
Manacher算法
string processStr(string s){
string str(2*s.length() + 1, '#');
for(int i = 0; i < s.length(); i++)
str[2*i+1] = s[i];
return str;
}
string longestPalindrome(string s) {
string str = processStr(s);
vector<int> vec_p(str.size(),1);
int max_d = 0;
int max_right = 0;
int max_length = 1;
int begin = 0;
for(int i = 0; i < str.size(); i++){
if(i < max_right){
vec_p[i] = vec_p[2*max_d - i]< max_right - i ?vec_p[2*max_d - i]: max_right - i;
}
else
vec_p[i] = 1;
while(i - vec_p[i] >= 0 && i + vec_p[i] < str.size() &&str[i - vec_p[i]] == str[i + vec_p[i]])
vec_p[i]++;
if(vec_p[i] + i - 1 > max_right){//因为最少长度为1
max_right = vec_p[i] + i - 1;
max_d = i;
}
if(max_length < vec_p[i]){
max_length = vec_p[i];
begin = i - max_length + 1;
}
}
string str_ret = str.substr(begin,2*max_length - 1);
str_ret.erase(std::remove(str_ret.begin(), str_ret.end(), '#'), str_ret.end());
return str_ret;
}
//中心点扩散法
string findLongestPalindrome(string &s)
{
const int length=s.size();
if(length == 1)return s;
if(length == 0)return NULL;
int maxlength=0;
int start;
for(int i=0;i<length;i++)//长度为奇数
{
int j=i-1,k=i+1;
while(j>=0&&k<length&&s.at(j)==s.at(k))
{
if(k-j+1>maxlength)
{
maxlength=k-j+1;
start=j;
}
j--;
k++;
}
}
for(int i=0;i<length;i++)//长度为偶数
{
int j=i,k=i+1;
while(j>=0&&k<length&&s.at(j)==s.at(k))
{
if(k-j+1>maxlength)
{
maxlength=k-j+1;
start=j;
}
j--;
k++;
}
}
if(maxlength>0)
return s.substr(start,maxlength);
return NULL;
}
2.给一个字符串 S, 你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
例如:
给出 "aacecaaa"
,返回 "aaacecaaa"
。
给出 "abcd"
,返回 "dcbabcd"
。
该题主要要理解题意,其实就是求回文的中心位置,该位置必须保证从原字符串第一个字符开始,所以可以用题一种的扩散法和Manacher算法改进一下求解,下面列出Manacher算法解题代码
class Solution {
public:
string shortestPalindrome(string s) {
string str = processStr(s);
vector<int> vec_p((str.size()+1)/2,1);
int max_d = 0;
int max_right = 0;
int cent = 0;
for(int i = 0; i < (str.size()+1)/2; i++){
if(i < max_right){
vec_p[i] = vec_p[2*max_d - i]< max_right - i ?vec_p[2*max_d - i]: max_right - i;
}
else
vec_p[i] = 1;
while(i - vec_p[i] >= 0 && i + vec_p[i] < str.size() &&str[i - vec_p[i]] == str[i + vec_p[i]])
vec_p[i]++;
if(vec_p[i] + i - 1 > max_right){//因为最少长度为1
max_right = vec_p[i] + i - 1;
max_d = i;
}
if(i + 1 == vec_p[i]){//注意这里表示必须能扩散到字符串头部。
//max_length = vec_p[i];
cent = i;
}
}
string str_add = str.substr(vec_p[cent]*2 - 1,str.size() - vec_p[cent]*2 + 1);//需要补全的
int add_len = str_add.size();
string new_str(add_len,' ');
for(int i = 0; i < add_len; i++)//反转
new_str[i] = str_add[add_len-1-i];
new_str += str;
new_str.erase(std::remove(new_str.begin(), new_str.end(), '#'), new_str.end());
return new_str;
}
string processStr(string s){
string str(2*s.length() + 1, '#');
for(int i = 0; i < s.length(); i++)
str[2*i+1] = s[i];
return str;
}
};
3.
给定一组
独特
的单词, 找出在给定列表中
不同
的索引对
(i, j)
,使得关联的两个单词,例如:
words[i] + words[j]
形成回文。
示例 1:
给定 words
= ["bat", "tab", "cat"]
返回 [[0, 1], [1, 0]]
回文是 ["battab", "tabbat"]
示例 2:
给定 words
= ["abcd", "dcba", "lls", "s", "sssll"]
返回 [[0, 1], [1, 0], [3, 2], [2, 4]]
回文是 ["dcbaabcd", "abcddcba", "slls", "llssssll"]
该题解题思路暂时只想到两种,一种就是暴力搜索,代码就不贴出来了。
第二种借助hash map,思路是将每个字符串进行循环分割,如果一部分为回文串,另一部分不是,则只需要搜索能否找到非回文串的反转穿,找到即可配对一组,代码如下:
class Solution {
public:
bool isvalid(string s){ //判断是否为回文串
int i = 0, j = s.size()-1;
while(i < j){
if(s[i] != s[j])
return false;
i++;
j--;
}
return true;
}
vector<vector<int>> palindromePairs(vector<string>& words) {
vector<vector<int>>res;
map<string, int>map_str;
for(int i = 0; i < words.size(); i++)
map_str[words[i]] = i;
for(int j = 0; j < words.size(); j++){
reverse(words[j].begin(), words[j].end());
int len = words[j].size();
for(int k = 0; k <= len; k++){
string left = words[j].substr(0,k);
string right = words[j].substr(k);
if(map_str.count(left) && isvalid(right)&&(map_str[left] != j)&&(k < len)) //从前面连接
res.push_back(vector<int>{map_str[left], j});
if(map_str.count(right) && isvalid(left)&&(map_str[right] != j)) //从后面连接
res.push_back(vector<int>{j, map_str[right]});
}
}
return res;
}
};