Skip to content

https://leetcode.com/problems/shortest-palindrome/

You are given a string s. You can convert s to a

palindrome by adding characters in front of it.

Return the shortest palindrome you can find by performing this transformation.

Example 1:

Input: s = "aacecaaa"
Output: "aaacecaaa"

Example 2:

Input: s = "abcd"
Output: "dcbabcd"

Constraints:

  • 0 <= s.length <= 5 * 104
  • s consists of lowercase English letters only.

Firstly, i use brute method to solve it, but get Time Limit.

c++
class Solution {
public:
    void change(int &left, int &right) {
        bool isEven = (left+right)%2==0;
        if(isEven){
            right--;
        }else{
            left--;
        }
    }
    string shortestPalindrome(string s) {
        //there is a string s. I need to add chs to convert it to a palindrome.
        //I only can add chs in front of s.
        //There many ways to form a palindrome by adding chs. But you must add fewest chs so that you get the shortest palindrome.
        //find the center ch. check if the chs on both sides are equal. Cal the num of chs that equal.
        //it must cannot meet any ch that does not equal
        //loop util one pointer arrives bound
        // init mid and let it close to left end;
        int n=s.size();
        int left, right;

        int mid=n/2;
        if(n%2==1) {
            left=mid-1;
            right=mid+1;
        }else{
            left=mid-1;
            right=mid;
        }
        int start=0;
        while(1) {
            int pleft=left, pright=right;
            //initial value for pleft and pright in every loop
            while(1) {
                // loop util pleft arrives bound or appear not equal
                if(pleft < 0 || s[pleft]!=s[pright]) {
                    break;
                }
                pleft--;
                pright++;
            }
            //if left does not arrive bound, then mid--
            if(pleft>=0){
                change(left, right);
            } else{
                start=pright;
                break;
            }
        }

        // add [start, end].reverse to s
        string sub=s.substr(start, n-1-start+1);
        for(int i=0; i<sub.size(); i++){
            s=sub[i]+s;
        }
        return s;
    }
};

Actually, this problem can be converted to find the longest common part and we only need the last comparison.

We compare [0:matched]==[i-matched, i]

We can use KMP to boost the process. Otherwise, every time we fail to match s[matched] and s[i]. We need to set i as N-1 and reset matched as 0.

c++
class Solution {
public:
    void buildArray(string &s, vector<int> &back) {
        back[0] = 0;
        for (int len = 0, i = 1; i < s.size();) {
            if (s[len] == s[i]) {
                //match then increase both and set table
                // len means the num of matched
                back[i] = len + 1;
                len++;
                i++;
            } else {
                if (len==0) {
                    i++;
                } else {
                    len = back[len - 1];
                }
            }
        }
    }

    string shortestPalindrome(string s) {
        if (s.empty()) {
            return s;
        }
        int N = s.size();
        vector<int> back(N);
        buildArray(s, back);
        int matched = 0;
        for (int i = N - 1; i >= 0;) {
            //check if match then increase
            if (s[matched] == s[i]) {
                matched++;
                i--;
            } else {
                //reset matched
                if (matched==0) {
                    i--;
                } else {
                    matched = back[matched - 1];
                }
            }
        }
        //sub=[matched:]
        string sub = s.substr(matched);
        reverse(sub.begin(),sub.end());
        s=sub+s;
        return s;
    }
};