题目大意
给定串S,要求在其前面(in front of)增加最少的串构成回文并输出回文串。
解题思路
席八,这个题目无愧其hard的标签,我想破脑袋也才想了个宽搜的暴力,而且还忽略了要加载S串前面的基本要求,审题不仔细,还是太浮躁了。这题没做出来,仔细琢磨了网上的解法,现总结如下:
先明确几个理论,以后想回文相关的题目从这几点入手:
1、回文串 = reverse(回文串)
2、回文串T+回文串T 为了减少长度可以压缩视为为一个回文串T
3、任何字符串S 经过镜面翻转必为回文串
以上三点看上去都特么是废话,但就是这种简单的常识往往难以被重视。回到这个题目,首先尝试构造一个最坏的解,根据第三点,将S串翻转,加到其前(为什么不是后面?因为题目要求要在前面加嘛。。。众人:碧池~~)。这样显然用最差的方式构建了一个长度为2*s.length()的回文串,因此需要对其进行优化,此时考虑上述第二点,如果S前缀和后缀都是回文串的话,这样翻转之后的串就能够被压缩了。。注意这里出现了前缀和后缀,前缀和后缀,前缀和后缀,重要的事情讲三遍,是的,前缀和后缀(众人:还来?滚粗!说重点),由此很是不是很容易想到通过KMP的next串的构造找到前缀和后缀相同的长度?因此重新构造字符串T=原串S,将T镜面翻转到后面构成T’(为什么这次翻转到后面,因为需要检测S前缀中回文长度,根据第一点,利用KMP匹配出来的前缀肯定是回文),这样通过KMP可以得出其前缀最长回文长度,最后只需要将原串中非回文的部分(长度为s.length()-next[T‘.length()-1])的部分翻转到串的最前面就可以满足题目的要求了,整体来讲应该是一种贪心的思路。
补充:
这里有个细节需要更正一下,首先感谢田神要努力填坑同志能发现这一问题,并给出合适的测试样例aabba。该算法存在一个细节问题,执行KMP匹配的时候不应当进行重复的匹配,这样会导致判断前缀回文的判断超过串本身的长度(因为是直接翻转后放置在后面,故而计算next数组时会将翻转串的部分也会比对进来),因此应当将翻转串和原串分开比对,略作修改就好
总结
首先题目要看懂,其次但凡涉及回文就要考虑上述三点基本特征
代码(存在问题)
class Solution {
int next[81000];
void generateNext(string s)
{
next[0] = 0;
for (int i = 1, j = 0; i < s.length(); i ++) {
while (j > 0 && s[j] != s[i]) j = next[j-1];
if (s[i] == s[j]) j ++;
next[i] = j;
}
}
public:
string shortestPalindrome(string s) {
int sLen = s.length();
if (sLen == 0)return "";
string cur = s;
string rs = s;
reverse(rs.begin(), rs.end());
cur += rs;
int curN = cur.length();
generateNext(cur);
int commonLen = next[curN-1];
if (commonLen > sLen)return s;
string res = s.substr(commonLen,sLen-commonLen);
reverse(res.begin(), res.end());
return (res+s);
}
};
代码(修改上段代码的错误)
<span style="font-size:14px;">class Solution {
int next1[41000];
int next2[41000];
void generateNext(string s1, string s2)
{
next1[0] = 0;
next2[0] = 0;
int len1 = s1.length();
for (int i = 1, j = 0; i < len1; i ++) {
while (j > 0 && s1[j] != s1[i]) j = next1[j-1];
if (s1[i] == s1[j]) j ++;
next1[i] = j;
} //这里使用翻转串s2去匹配原串s1
for (int i = 0, j = 0; i < len1; i ++) {
while (j > 0 && s2[i] != s1[j]) j = next1[j-1];
if (s2[i] == s1[j]) j ++;
next2[i] = j;
}
}
public:
string shortestPalindrome(string s) {
int sLen = s.length();
if (sLen == 0)return "";
string cur = s;
string rs = s;
reverse(rs.begin(), rs.end());
//cur += rs;
int curN = cur.length();
generateNext(cur, rs);
int commonLen = next2[curN-1];
if (commonLen > sLen)return s;
string res = s.substr(commonLen,sLen-commonLen);
reverse(res.begin(), res.end());
return (res+s);
}
}; </span>