首页 > 其他 > 详细

Longest Palindromic Subsequence

时间:2014-03-19 17:42:55      阅读:460      评论:0      收藏:0      [点我收藏+]

Longest Palindromic Subsequence (not substring, for the substring solution, please find answers in my GitHub) is to find (one of) the longest palindromic subsequence in a string. It is short for LPS in this article (here we only need to find one of LPS). See an example from another article.

  s = 1 5 2 4 3 3 2 4 5 1
LPS = 1 5   4 3 3   4 5 1

Ok, I think now you are clear about the definition of the LPS (again not substring). Let‘s start our related algorithms and let them evolve.


I. Naive Recursive Version, time complexity O(2^n) (worst case, O(n)=2O(n-1)+1), space complexity O(n) = O(h) for stack operation.

string longestPalindrome(const string &s, int begin, int end) {
	if (begin > end) return "";
	if (begin == end) return s.substr(begin, 1);
	if (s[begin] == s[end]) return s[begin] + longestPalindrome(s, begin+1, end-1) + s[end];
	string str1 = longestPalindrome(s, begin, end-1);
	string str2 = longestPalindrome(s, begin+1, end);
	return str1.size() > str2.size()? str1: str2;
}

string longestPalindrome(string s) {
	return longestPalindrome(s, 0, s.size()-1);
}

 

Here we return immediately once we find the s[begin] == s[end] holds, ignoring s[begin+1, end] and s[begin, end-1]

Why? Does this mean s[begin] and s[end] are parts of the LPS we are seeking for if s[begin] == s[end] holds?  


1. We can guarantee that s[begin, end] has the LPS. 

  • But it is possible we get the same LPS from s[begin+1, end] or s[begin, end-1], such as "asaa" (LPS: asa)
  • However, LPS‘ from s[begin+1, end] or s[begin, end-1] is <= LPS from s[begin, end]. It is simply because s[begin, end] is longer than s[begin+1, end] or s[begin, end-1].

2. s[begin] and s[end] are parts of s[begin, end]‘s LPS, head and tail respectively. If this does not hold, we have two situations.

  • s[begin, end]‘s LPS does not have s[begin] nor s[end]. We know it is not true as s[begin] + LPS + s[end]  > LPS. So the real LPS should be s[begin] + LPS + s[end]not LPS.
  • s[begin, end]‘s LPS contains only one of s[begin] and s[end]. It is true in certain cases, such as "asaa" (LPS: asa). But In these cases, LPS.head == LPS.tail holdssince palindrome property. If LPS contains s[begin] (vice versa for s[end]), LPS.tail == LPS.head ==  s[begin] == s[end]. So rule 2 still holds.

II. Top-down Dynamic Programming Version, time complexity O(n^2) (worst case, O(n)=O(n-1)+n), space complexity O(n^2).
string longestPalindrome(const string &s, int begin, int end, vector<vector<string>> &mem) {
	if (begin > end) return "";
	if (mem[begin][end] != "#") return mem[begin][end];
	if (begin == end) return mem[begin][end].assign(s, begin, 1);
	if (s[begin] == s[end]) return mem[begin][end] = s[begin] + longestPalindrome(s, begin+1, end-1, mem) + s[end];
	string str1 = longestPalindrome(s, begin, end-1, mem);
	string str2 = longestPalindrome(s, begin+1, end, mem);
	return mem[begin][end] = str1.size() > str2.size()? str1: str2;
}

string longestPalindrome(string s) {
	int N = s.size();
	vector<vector<string>> mem(N, vector<string>(N, "#"));
	return longestPalindrome(s, 0, N-1, mem);
}


III. Bottom-up Dynamic Programming Version, time complexity O(n^2), space complexity O(n^2).

string longestPalindrome(string s) {
	if (s.empty()) return s;
	int N = s.size();
	vector<vector<string>> mem(N, vector<string>(N));
	for (int i=N-1; i>=0; --i) {
		for (int j=i; j<N; ++j) {
			if (s[i] == s[j]) {
				if (j-i <= 1) mem[i][j].assign(s, i, j-i+1);
				else mem[i][j] = s[i] + mem[i+1][j-1] + s[j];
			} else {
				mem[i][j] = mem[i][j-1].size()>=mem[i+1][j].size()? mem[i][j-1]: mem[i+1][j];
			}
		}
	}
	return mem[0][N-1];
}


IV. Space Optimized Bottom-up Dynamic Programming Version, time complexity O(n^2), space complexity O(n).

string longestPalindrome(string s) {
	if (s.empty()) return s;
	int N = s.size();
	vector<vector<string>> mem(2, vector<string>(N));
	for (int i=N-1; i>=0; --i) {
		for (int j=i; j<N; ++j) {
			if (s[i] == s[j]) {
				if (j-i <= 1) mem[i%2][j].assign(s, i, j-i+1);
				else mem[i%2][j] = s[i] + mem[(i+1)%2][j-1] + s[j];
			} else {
				mem[i%2][j] = mem[i%2][j-1].size()>=mem[(i+1)%2][j].size()? mem[i%2][j-1]: mem[(i+1)%2][j];
			}
		}
	}
	return mem[0][N-1];
}


V. Tracing Bottom-up Dynamic Programming Version, time complexity O(n^2), space complexity O(n^2).

/***************** This version is used when string is very large ***************/

// time complexity O(n), space complexity O(n)
string getLPS(vector<vector<char>> &trace, int i, int j, const string &s) {
	if (i > j) return "";
	if (i == j) return s.substr(i, 1);
	if (trace[i][j] == ‘#‘) return s[i] + getLPS(trace, i+1, j-1, s) + s[j];
	if (trace[i][j] == ‘-‘) return getLPS(trace, i, j-1, s);
	if (trace[i][j] == ‘|‘) return getLPS(trace, i+1, j, s);
}

// time complexity O(n^2), space complexity O(n^2)
int longestPalindrome(string s, string &res) {
	if (s.empty()) return 0;
	int N = s.size();
	vector<vector<int>> mem(2, vector<int>(N));
	vector<vector<char>> trace(N, vector<char>(N, ‘#‘));
	for (int i=N-1; i>=0; --i) {
		for (int j=i; j<N; ++j) {
			if (s[i] == s[j]) {
				if (j-i <= 1) mem[i%2][j] = j - i + 1;
				else mem[i%2][j] = mem[(i+1)%2][j-1] + 2;
			} else {
				mem[i%2][j] = max(mem[i%2][j-1], mem[(i+1)%2][j]);
				trace[i][j] = mem[i%2][j-1]>=mem[(i+1)%2][j]? ‘-‘: ‘|‘;
			}
		}
	}
	res = getLPS(trace, 0, N-1, s);
	return mem[0][N-1];
}


Longest Palindromic Subsequence,布布扣,bubuko.com

Longest Palindromic Subsequence

原文:http://blog.csdn.net/jsc0218/article/details/21523891

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!