[leetcode] 214. Shortest Palindrome 解题报告

题目链接:https://leetcode.com/problems/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".



思路:让在前面补一些字符使得给定的字符串变成回文,观察可以发现我们需要添加多少个字符与给定字符串的前缀子串回文的长度有关.也就是说去掉其前缀的回文子串,我们只需要补充剩下的子串的逆序就行了,举个栗子:aacecaaa,其前缀的最大回文子串是aacecaa,剩下了一个a,因此我们只需要在前面加上一个a的逆序a就行了.再例如abcd,其前缀的最大回文是a,因此剩下的子串就为bcd,所以需要在前面加上bcd的逆序,就变成了dcbabcd.所以这样问题就转化为求字符串的前缀最大回文长度.

OK,思路是这样,接下来就要看怎么实现了,一个naive的方法是先判断整个字符串是否回文,否的话再判断前n-1个子串是否回文,这样依次缩减长度,直到找到一个回文子串就是最大的前缀回文子串.这种方法简单粗暴,容易理解和实现,如果在面试中要求不是很严格的情况下说不定可以过关, 反正总比不会好点^.^.其时间复杂度是O(n!).

当然问题不会这么简单,接下来的才是真正有效的解法,KMP.这是一个非常高效的字符串比较算法.其原理是给定一个字符串S和P,要在S中寻找是否存在P,一般的方法是逐位比较,如果不能完全匹配,则S再回到开始位置向右移动一位,P回到0位置再开始比较.在KMP中不需要回到首部重新开始比较,借助与记录从P的开头到当前位置P中的前缀和后缀有多少位是相等的,这样当P和S比较的时候如果P[i] != S[j]了,不需要回到P[0]的位置重新比较,我们可以查看P中已经匹配过的子串中(也就是P[0, i-1]子串)前缀和后缀有多少位是相等的,然后将P的前缀和已经S[j]之前的后缀是匹配的,就可以不用回溯S了.





所以借助与KMP记录最长前缀和后缀的方法,我们可以将原字符串翻转以后加在原字符串的后面,其最大的前缀和后缀就是前缀的最大回文长度.我们还需要在这两个字符串之间加一个冗余字符,因为形如aaaaa这种字符串如果不加一个冗余字符最大前缀和后缀会变大.

代码如下:

class Solution {
public:
    string shortestPalindrome(string s) {
        string str = s;
        reverse(str.begin(), str.end());
        str = s+ "#" + str;
        int len1 = s.size(), len2 = str.size();
        vector vec(len2, 0);
        for(int i = 1; i < len2; i++)
        {
            int k = vec[i-1];
            while(k > 0 && str[k] != str[i]) k = vec[k-1];
            vec[i] = (k += str[i] == str[k]);
        }
        return str.substr(len1+1, len1-vec[len2-1]) + s;
    }
};
参考:https://leetcode.com/discuss/36807/c-8-ms-kmp-based-o-n-time-%26-o-n-memory-solution

http://wiki.jikexueyuan.com/project/kmp-algorithm/define.html

你可能感兴趣的:(leetcode,KMP,string)