LeetCode题214 —— 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.

Example 1:

Input: "aacecaaa"
Output: "aaacecaaa"

Example 2:

Input: "abcd"
Output: "dcbabcd"

思路

最简单直接的方法是 brute force,即先求以起始索引为开始位的最长回文字串,然后将剩下的部分逆置直接加到s前即可。这种方法比较耗时,可以使用KMP算法来解这题。

KMP算法

将s和其逆转字符串r连接起来,这肯定是一个回文,在他们中间加一个其它字符构成新的字符串t,然后还需要一个和t长度相同的一位数组next,其中next[i]表示从t[i]到开头的子串的相同前缀后缀的个数,就是KMP算法中的那个next数组。最后把不相同的个数对应的字符串添加到s之前即可。其实和brute force一样都是找以起始索引为开始位的最长回文字串,只不过现在不是遍历各个字符去找,而是利用KMP算法去找。找到后直接将原字符串s除了这个回文字串剩下的部分逆置后加到s前面即可。

下面next数组的计算过程可以参照KMP算法解释连接中最大长度表部分。

对于next[i-1],如果是0则表示0到索引i-1位置上的字串中所有的前缀和后缀都不匹配,甚至是起始索引处的单个字符和末尾索引处的单个字符都匹配不了,那么在这种情况下加入索引i位置上的字符得到的0~i的字串情况又是如何呢?首先是对于原来最长的前缀后面加上了索引i-1位置上的字符构成了新的最长前缀,而原来的最长后缀后面加上了位置i上的索引构成了新的最长后缀,其余的前缀后缀不变。但是因为原来的前缀后缀本身就不匹配了所以新的前缀后缀肯定是不匹配的。但是有一种可能会增加前缀后缀的公共最长元素,那就是原来的起始索引处的字符可能等于新加入的i索引位置上的字符,若相等则next[i]=1,否则还是0。

假如next[i-1]不是0,则其值是0到索引i-1位置上的字串中前缀后缀的最大公共元素长度(能够匹配的最长前缀后缀),假设这个前缀后缀是 q。这时新加了索引i位置上的字符,原来最长前缀后面加上了索引i-1位置上的字符构成了新的最长前缀,其余前缀(包括原来的最长前缀)都保持不变。原来的所有后缀后面加上了位置i上的索引构成了新的后缀,并且后缀中还要替换末尾索引处的单个字符。

next[i] -- 0~i处匹配的最长前缀后缀的长度。

代码

解释

public String shortestPalindrome(String s) {
    String temp = s + "#" + new StringBuilder(s).reverse().toString();
    int[] table = getTable(temp);
    
    //get the maximum palin part in s starts from 0
    return new StringBuilder(s.substring(table[table.length - 1])).reverse().toString() + s;
}

public int[] getTable(String s){
    //get lookup table
    int[] table = new int[s.length()];
    
    //pointer that points to matched char in prefix part
    int index = 0; 
    //skip index 0, we will not match a string with itself
    for(int i = 1; i < s.length(); i++){
        if(s.charAt(index) == s.charAt(i)){
            //we can extend match in prefix and postfix
            table[i] = table[i-1] + 1;
            index ++;
        }else{
            //match failed, we try to match a shorter substring
            
            //by assigning index to table[i-1], we will shorten the match string length, and jump to the 
            //prefix part that we used to match postfix ended at i - 1
            index = table[i-1];
            
            while(index > 0 && s.charAt(index) != s.charAt(i)){
                //we will try to shorten the match string length until we revert to the beginning of match (index 1)
                index = table[index-1];
            }
            
            //when we are here may either found a match char or we reach the boundary and still no luck
            //so we need check char match
            if(s.charAt(index) == s.charAt(i)){
                //if match, then extend one char 
                index ++ ;
            }
            
            table[i] = index;
        }
        
    }
    
    return table;
}

猜你喜欢

转载自www.cnblogs.com/f91og/p/9465840.html