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.

For example:

Given "aacecaaa", return "aaacecaaa".

Given "abcd", return "dcbabcd".


可以在给定的字符前面添加字串,添加最短的字串使之成为回文串。

首先,要找一个最长的回文串,这个回文串以某个字符为中心或者以某个字符之间的空白位置为中心,且回文串的左端一直延伸到给定字串的开始部分,一定要延伸到开始部分,这样才能将找到的回文串右端到给定字串右端的部分对称到左端形成回文串。这样处理起来添加的字符是最少的。

 public static String shortestPalindrome(String s)
	{
		int len=s.length();
		if(len<1)
			return "";
		
		int index=-1;
		int ispali=0;
		for(int i=len/2;i>=0;i--)
		{
			ispali=2;
			for(int j=i+1,k=i;k>=0;j++,k--)
				if(j==len||s.charAt(j)!=s.charAt(k))
				{
					ispali=0;
					break;
				}
			
			if(ispali==0)
			{
				ispali=1;
				for(int j=i+1,k=i-1;k>=0;j++,k--)
					if(j==len||s.charAt(j)!=s.charAt(k))
					{
						ispali=0;
						break;
					}
			}
				
			if(ispali!=0)
			{
				index=i;
				break;
			}
		}
		StringBuilder sb=new StringBuilder();
		
		if(ispali>0)
		{
			sb.append(s);
			for(int i=ispali==2?(index+1)*2:index*2+1;i<len;i++)
				sb.insert(0, s.charAt(i));
		}
		else {
			sb.append(s.substring(1));
			sb.reverse();
			sb.append(s);
		}
		
		
		return sb.toString();
			
	}


---------------------------------------------------------------------------------------------

另外还有一种方法,需要O(N^2)的空间,但是思路清晰,不仅可以在前面添加字串,也可以在尾部添加字串成为回文串,有时候在尾部添加能够有更少的步数,不过题目并没有这样要求。

先用动态规划求出[i,j]是否是回文串,然后找出从0开始的最长回文串和以len-1结尾的最长回文串,再把非回文部分对称补齐即可。

动归判断是否回文的时候dp[i][j]=dp[i+1][j-1]&s.charAt(i)==s.charAt(j),i依赖于i+1所以 i 逆序

public static String shortestPalindrome(String s)
	{
		int len=s.length();
		boolean[][] dp=new boolean[len][len];
		for(int i=len-1;i>=0;i--)
			for(int j=0;j<len;j++)
			{
				if(i>=j)
					dp[i][j]=true;
				else {
					dp[i][j]=dp[i+1][j-1]&&(s.charAt(i)==s.charAt(j));
				}
			}
		
		int maxfrom0=Integer.MIN_VALUE;
		int maxendlen=Integer.MIN_VALUE;
		
		for(int i=1;i<len;i++)
			if(dp[0][i])
				maxfrom0=i;
		
		for(int i=len-2;i>=0;i--)
			if(dp[i][len-1])
				maxendlen=i;
		
		
		int max=Math.max(maxfrom0==Integer.MIN_VALUE?maxfrom0:maxfrom0+1, maxendlen==Integer.MIN_VALUE?Integer.MIN_VALUE:len-maxendlen);
		StringBuilder sb=new StringBuilder(s);
		
		
		
		if(max==Integer.MIN_VALUE)
		{
			for(int i=1;i<len;i++)
				sb.insert(0, s.charAt(i));
		}
		else {
			if(max==maxfrom0+1)
			{
				for(int i=maxfrom0+1;i<len;i++)
					sb.insert(0, s.charAt(i));
			}
			else {
				for(int i=maxendlen-1;i>=0;i--)
					sb.append(s.charAt(i));
			}
		}
		
		
		if(max==Integer.MIN_VALUE)
		{
			for(int i=1;i<len;i++)
				sb.insert(0, s.charAt(i));
		}
		else {
	
			for(int i=maxfrom0+1;i<len;i++)
				sb.insert(0, s.charAt(i));
		}
		
		return sb.toString();
	}


----------------------------------------------------------------------------------------------------------------


https://discuss.leetcode.com/topic/27261/clean-kmp-solution-with-super-detailed-explanation/2

http://blog.csdn.net/yujin753/article/details/47047155

http://www.tuicool.com/articles/Uv6byyU

http://blog.csdn.net/pointbreak1/article/details/45931551

----------------------------------------------------------------------------------------------------------------

记原始字符串为s,s的反转字符串为rev_s。

构造字符串l = s + '#' + rev_s,其中'#'为s中不会出现的字符,添加'#'是为了处理输入为空字符串的情形。

对字符串l执行KMP算法,可以得到“部分匹配数组”p(也称“失败函数”)

我们只关心p数组的最后一个值p[-1],因为它表明了rev_s与s相互匹配的最大前缀长度。

最后只需要将rev_s的前k个字符与原始串s拼接即可,其中k是s的长度len(s)与p[-1]之差。



---------------------------------------------------------------------------------------------------------------

其实求最短回文串,其实可以看作两个字符串求两串中的最长匹配字符,比如 串 “ abcd”

  • 注意,由于这里一个串可以是回文串,所以此处的前缀和后缀应该分别加上最后一个和第一个字符

就是求“abcd”和反串”dcba“的前缀和后缀最大匹配长度

原始串前缀反转串后缀最大匹配
abcda ab abc abcddcbaa ba cba dcbaa

由上面可以看出,abcd和dcba的最长匹配为a,一个字符,那么最后的回文串就是 反转串的长度4减去匹配长度1,得到3, 即反转串的前三个字符加上 原始串组成 ”abcabcd“

引子:我们在 KMP算法中曾今和相似的利用过最大前缀和后缀求next[]数组,如果我们这样看 将原始串S和反转串R形成一个新串New

S+#+反转   =  abcd#dcba 

这里的#表示一个s中不存在的字符,然后求这个新串的部分匹配数组P[],求的p[New.length()-1]的值,这里的前缀和后缀就不需要加最后一个和第一个,因为现在已经是一个字符串

新串前缀后缀最大匹配
abcd#dcbaa、ab、abc、abcd、abcd# 、abcd#d、abcd#dc、abcd#dcba,ba, cba,dcba,#dcba, d#dcba,cd#dcba,bcd#dcbaa
  • 注意这里之所以要加上#是为了防止p[New.length()-1]的值要大于s.length()

代码如下:

class Solution {
public:
    string shortestPalindrome(string s) {
        string r = s;
        reverse(r.begin(), r.end());
        string t = s + "#" + r;
        vector<int> p(t.size(), 0);
        for (int i = 1; i < t.size(); ++i) {
            int j = p[i - 1];
            while (j > 0 && t[i] != t[j]) j = p[j - 1];
            p[i] = (j += t[i] == t[j]);
        }
        return r.substr(0, s.size() - p[t.size() - 1]) + s;
    }
};


----------------------------------------------------------------------------------------------------------------

先制作原字符串的对称镜像字符串,如s = "abcd", 镜像a = "abcddcba"。

然后对新字符串a,按KMP算法求Prefix的方法,求Prefix, 得【0, 0, 0, 0, 0,  0, 0, 1】,然后s.length() - prefix[2 * s.length()-1] 即为需要复制到s前的s尾部字符串的个数。



public class Solution {  
    public String shortestPalindrome(String s) {  
        String result = "";  
        if(s.length() == 0)  
            return result;  
        int[] prefix = new int[s.length() * 2];  
        String mirror = s + new StringBuilder(s).reverse().toString();  
        for(int i = 1; i < s.length() * 2; i++) {  
            int j = prefix[i-1];  
            while(mirror.charAt(j) != mirror.charAt(i) && j > 0)  
                j = prefix[j-1];  
            if(mirror.charAt(i) == mirror.charAt(j))  
                prefix[i] = j + 1;  
            else  
                prefix[i] = 0;  
        }  
        int count = s.length() - prefix[s.length() * 2 -1];  
        result = new StringBuilder(s.substring(s.length()-count, s.length())).reverse().toString() + s;  
        return result;  
    }  
}  


----------------------------------------------------------------------------------------------------------------


ublic 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值