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 | a ab abc abcd | dcba | a ba cba dcba | a |
S+#+反转 = abcd#dcba
新串 | 前缀 | 后缀 | 最大匹配 |
abcd#dcba | a、ab、abc、abcd、abcd# 、abcd#d、abcd#dc、abcd#dcb | a,ba, cba,dcba,#dcba, d#dcba,cd#dcba,bcd#dcba | a |
- 注意这里之所以要加上#是为了防止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;
}