题目大意
给定串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>