给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
示例 1:
输入: "aacecaaa"
输出:"aaacecaaa"
示例 2:
输入:"abcd"
输出:"dcbabcd"
这题考虑把字符串s
分成s1
和s2
两部分,即s=s1+s2
,其中s1
为回文串,s2
不是回文串,那么构成的最短回文串就是reverse(s2.begin(),s2.end())+s
。所以这道题最大的难点就是找出从起始位置开始的最长回文子串。关于最长回文子串的解法可以参考最长回文子串(https://blog.csdn.net/lv1224/article/details/81051875),
DP做法是要考虑所有可能的回文串,但是这题只需要考虑从起始位置开始的字符串就可以了,所以直接考虑从起始位置开始的所有字符串,判断是否是回文串即可,注意从大往小考虑,如果构成回文串,剩下的就可以不用考虑
代码
class Solution {
public:
string shortestPalindrome(string s) {
int n=s.size(),maxlen=0;
if(n<=1) return s;
int r=n-1;
while(r>0){
int i=0,j=r;
while(j>i&&s[i]==s[j]){
i++;j--;
}
if(i>=j) break;
r--;
}
string s1=s.substr(r+1,n-r-1);
reverse(s1.begin(),s1.end());
return s1+s;
}
};
直接采用中心扩展来做,会有一个case过不了,这里有一个小技巧。中心点不需要从起始位置开始,也不需要遍历所有的,只要找到最大的就可以停止了。n=s.size()
,那么回文串最大就是s
本身,所以只需要从n/2
的位置开始往左遍历即可,一旦找到从起始位置开始的回文串,就可以停止寻找,得出答案。这样可以减少程序运行时间。
class Solution {
public:
string shortestPalindrome(string s) {
int n=s.size(),maxlen=0;
if(n<=1) return s;
//这里会有一个小技巧
for(int i=n/2;i>=0&&maxlen<=0;i--){
int r=1;
while(i-r>=0&&i+rif(maxlen<2*r-1&&i-r+1==0)
maxlen=2*r-1;
if(i>=n||s[i]!=s[i+1]) continue;
r=1;
while(i-1>=0&&i+1+r1+r]) r++;
if(maxlen<2*r&&i-r+1==0)
maxlen=2*r;
}
string s1=s.substr(maxlen,n-maxlen);
reverse(s1.begin(),s1.end());
return s1+s;
}
};
static const auto __ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
return nullptr;
}();
class Solution {
public:
string shortestPalindrome(string s) {
int ns=s.size();
if(ns<=1) return s;
string tmp(2*ns+1,'*');
for(int i=0;i2*i+1]=s[i];
int pos=0,curlen=0,n=tmp.size();
int maxr=0;
vector<int> slen(n,0);
for(int i=0;iint mi=2*pos-i,r=mi>=0?slen[mi]:0;
if(icontinue;
}
if(i>=pos+curlen) r=1;
else r=pos+curlen-i;
while(i+r=0&&(tmp[i+r]==tmp[i-r])) r++;
slen[i]=r-1;
pos=i;
curlen=r-1;
if(maxr<=r-1&&i-slen[i]==0){
maxr=r-1;
}
if(i+r==tmp.size()) break;
}
string s1=s.substr(maxr,ns-maxr);
reverse(s1.begin(),s1.end());
return s1+s;
}
};
这一种方法是比较巧妙的,r=s;reverse(r.begin(),r.end());t=s+"#"+r
,要求s
从起始位置开始的最长回文子串,就可以转换成t
的最长前缀后缀问题;
比如s=ccab,r=bacc,t=ccab#bacc
,那么s
中最长的回文串cc
也就是t
中前缀cc
。
再比如s=aacecaaa,r=aaacecaa,t=aacecaaa#aaacecaa
,则最长回文子串是aacecaa
,而t
中前缀为aacecaa
。
这里要特别注意需要加上#
,这个很重要,防止s=aaa
的时候出现错误。
所以这题其实就演变成KMP算法,求解next
数组。具体原理见KMP算法(链接:https://blog.csdn.net/v_july_v/article/details/7041827/)
代码;
class Solution {
public:
string shortestPalindrome(string s) {
string r=s;
reverse(r.begin(),r.end());
string t=s+"#"+r;
int n=t.size();
vector<int> idx(n,-1);
for(int i=1;iint p=idx[i-1];
while(p!=-1&&t[i]!=t[p+1])
p=idx[p];
if(p==-1) idx[i]=t[i]==t[0]?0:-1;
else idx[i]=p+1;
}
string tmp=s.substr(idx[n-1]+1,s.size()-idx[n-1]-1);
reverse(tmp.begin(),tmp.end());
return tmp+s;
}
};