2015-10-18 10:42:52 +08:00

194 lines
5.6 KiB
C++

// Source : https://leetcode.com/problems/shortest-palindrome/
// Author : Hao Chen
// Date : 2015-06-11
/**********************************************************************************
*
* 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".
*
* Credits:Special thanks to @ifanchu for adding this problem and creating all test cases.
* Thanks to @Freezen for additional test cases.
*
**********************************************************************************/
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
bool isPalindrome(string& s, int begin, int end) {
for (; begin<end; begin++, end-- ) {
if ( s[begin] != s[end] ) {
return false;
}
}
return true;
}
// Burst Force - - Time Limit Error
string shortestPalindrome_bf(string& s) {
int len = s.size();
int i;
for (i=len-1; i>=0; i--) {
if (isPalindrome(s, 0, i)) {
i++;
break;
}
}
string t = s.substr(i, len-i);
reverse(t.begin(), t.end());
return t+s;
}
//Dynamic Programming - Memory Limit Exceeded & Time Limit Error
// Using the method of finding longest palindrome
string shortestPalindrome_dp(string& s) {
int len = s.size();
if (len <=1 ) return s;
//find longest palindrome, see "Longest Palindromic Substring"
vector< vector<bool> > dp(len, vector<bool>(len, false));
for (int i=len-1; i>=0; i--){
for(int j=i; j<len; j++){
if (i==j || ((j-i==1 || dp[i+1][j-1]) && s[i]==s[j]) ) {
dp[i][j] = true;
}
}
}
/*
* // only use two lines -- still have Time Limit Error
*
* vector< vector<bool> > dp(2, vector<bool>(len, false));
* for (int i=len-1; i>=0; i--){
* for(int j=i; j<len; j++){
* if (i==j || ((j-i==1 || dp[(i+1)%2][j-1]) && s[i]==s[j]) ) {
* dp[i%2][j] = true;
* }
* }
* }
*/
//find the longest palindrome which start from first char.
int pos = 0;
for (int i=1; i<len; i++) {
if (dp[0][i]) {
pos = i+1;
}
}
string t = s.substr(pos, len - pos);
reverse(t.begin(), t.end());
return t+s;
}
/*
* The core of KMP algorithm needs to build a partial match table for pattern,
* which can determin how many chars need to skip if mismatch happens.
*
* The best explaination of the idea of the KMP algorithm is:
* http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/
*
* Here, we can use KMP algorithm to solve this problem.
*
* 1) We need construct a mirror string base on original string. and use a uniq char for seperator.
*
* considering the original string is "abab", then the mirror string is "abab#baba" (# is seperator)
*
* 2) Build the KMP partial match table for this pattern.
*
* a b a b # b a b a
* 0 0 1 2 0 0 1 2 3
*
* 3) Checking the last position of partial match table, it is 3. it means:
*
* For the entire pattern (mirror string), there has max 3 length of sub-string is both prefix and suffix.
*
* This sub-string is "aba".
*
* 4) The result of 3) is really useful to solve this shorest palindrome problem.
*
* Because "aba" is the pattern which in original string and its reversed version.
*
* ( Note: The uniq char is used for making the noise, which can make sure
* the original string and its reversed version won't be mixed.
* then, the common prefix and suffix sub-string is right )
*
* So, if a sub-string "aba" is existed in both original string and its reversed version,
* which means, this is the longest prefix palindrome.
*
* 5) Then, we can take rest suffix, and reverse it and put it in front of original string.
*
*/
string shortestPalindrome_kmp(string& s) {
int len = s.size();
if(len <= 1) return s;
string pattern = s + '\0' + string(s.rbegin(), s.rend());
//construct the partial match table
vector<int> prefix( pattern.size() );
prefix[0] = 0;
for(int i = 1; i < prefix.size(); i++) {
int j = prefix[i-1];
while( j > 0 && pattern[i] != pattern[j] ) {
j = prefix[j-1];
}
if ( pattern[i] == pattern[j] ) {
j++;
}
prefix[i] = j;
}
#ifdef _DEBUG
cout << endl;
for(int i=0; i<pattern.size(); i++){
cout << (pattern[i] ? pattern[i] : '#') << " ";
}
cout << endl;
for(int i=0; i<prefix.size(); i++) {
cout << prefix[i] << " ";
}
cout << endl;
cout << "-->" << s.substr(0, prefix.back()) << " " << s.substr(prefix.back()) << endl;
#endif
int pos = s.size() - prefix.back();
return string(s.rbegin(), s.rbegin() + pos) + s;
}
string shortestPalindrome(string s) {
return shortestPalindrome_kmp(s);
return shortestPalindrome_dp(s); //Memory Limit Error
return shortestPalindrome_bf(s); //Time Limit Error
}
#define TEST(s) cout << s << " : " << shortestPalindrome(s) << endl
int main(int argc, char**argv)
{
string s = "aabba";
if (argc>1){
s = argv[1];
}
TEST(s);
TEST("a");
TEST("ab");
TEST("aba");
TEST("abab");
TEST("aabb");
TEST("aacecaaa");
TEST("abcd");
TEST("aaabcb");
return 0;
}