1. 程式人生 > >LPS(最長回文子序列)

LPS(最長回文子序列)

轉化 最長回文 反轉 may substring 子串 def 平均情況 put

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example:

Input: "babad"

Output: "bab"

Note: "aba" is also a valid answer.

Example:

Input: "cbbd"

Output: "bb"

  這也是《算法導論》中的一個練習題,先給一個自己想的算法(最壞情況的時間復雜度為O(n2)):

class Solution {
public:
    string longestPalindrome(string s) {
        if (s.size() == 0)
			return 0;
		
		int i = 0, j = s.size() - 1;
		int flag = 0;
		int subscript = j;

		string s1 = "";

		while (i < j)
		{
			while (i < j)
			{
				if (s[i] == s[j])
				{
					s1 += s[j];
					subscript = --j;
					i++;
					flag = 1;
					break;
				}
				j--;
				flag = 0;
			}
			if (!flag)
			{
				j = subscript;
				i++;
			}
		}

		int k = 0, l = s.size() - 1;
		flag = 0;
		subscript = l;

		string str = s;
		reverse(str.begin(), str.end());
		string s2 = "";

		while (k < l)
		{
			while (k < l)
			{
				if (str[k] == str[l])
				{
					s2 += str[l];
					subscript = --l;
					k++;
					flag = 1;
					break;
				}
				l--;
				flag = 0;
			}
			if (!flag)
			{
				l = subscript;
				k++;
			}
		}
		int l1 = s1.size(), l2 = s2.size();
		s1 = l2 > l1 ? s2 : s1;

		if (i >= k && i > j)
		{
			string str = s1;
			reverse(str.begin(), str.end());
			s1 += str;
		}
		else if (k > i && k > l)
		{
			string str = s1;
			reverse(str.begin(), str.end());
			s1 += str;
		}
		else
		{
			string str = s1;
			reverse(str.begin(), str.end());
			s1 += s[l1 >= l2 ? i : k];
			s1 += str;
		}

		return s1;
    }
};

  兩段二重循環的目的是:第一段從右到左縮短str來得到回文子串s1;第二段將原來的字符串反轉,然後同樣的方式得到一個回文子串s2,然後返回最長的。

  但結果Leetcode以這種方式拒絕了:

Input:
"abcda"
Output:
"ada"
Expected:
"a"

  但我不覺得我的錯了啊...書上說:character,返回:carac,但在Leetcode竟然錯了。。。看來和書上問題並不太一樣。

  完整代碼(可以返回最長回文子串的長度):

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

class Solution {
public:
	std::string LPS(std::string str)
	{
		if (str.size() == 0)
			return 0;
		
		int r1 = 1;
		int i = 0, j = str.size() - 1;
		int flag = 0;
		int subscript = j;

		std::string s1 = "";

		while (i < j)
		{
			while (i < j)
			{
				if (str[i] == str[j])
				{
					s1 += str[j];
					subscript = --j;
					i++;
					r1++;
					flag = 1;
					break;
				}
				j--;
				flag = 0;
			}
			if (!flag)
			{
				j = subscript;
				i++;
			}
		}

		int r2 = 1;
		int k = 0, l = str.size() - 1;
		flag = 0;
		subscript = l;

		std::string s = str;
		std::reverse(s.begin(), s.end());
		std::string s2 = "";

		while (k < l)
		{
			while (k < l)
			{
				if (s[k] == s[l])
				{
					s2 += s[l];
					subscript = --l;
					k++;
					r2++;
					flag = 1;
					break;
				}
				l--;
				flag = 0;
			}
			if (!flag)
			{
				l = subscript;
				k++;
			}
		}
		int l1 = s1.size(), l2 = s2.size();
		s1 = l2 > l1 ? s2 : s1;

		if (i >= k && i > j)
		{
			std::string s = s1;
			std::reverse(s.begin(), s.end());
			s1 += s;
			r1--;
		}
		else if (k > i && k > l)
		{
			std::string s = s1;
			std::reverse(s.begin(), s.end());
			s1 += s;
			r2--;
		}
		else
		{
			std::string s = s1;
			std::reverse(s.begin(), s.end());
			s1 += str[l1 >= l2 ? i : k];
			s1 += s;
		}

		r1 *= 2;
		r1 = i > j ? r1 : r1 - 1;

		r2 *= 2;
		r2 = k > l ? r2 : r2 - 1;

		r1 = l1 >= l2 ? r1 : r2;
		std::cout << r1 << std::endl;
		return s1;
	}
};

int main()
{
	Solution solve;
	std::string str = "abcda";

	std::cout << str << std::endl;
	std::cout << solve.LPS(str) << std::endl;
	getchar();
	return 0;
}

  最開始我想到的辦法是通過反轉s,得到字符串s‘,然後把問題轉化成求LCS,但我發現不符合書上要求。但看樣子,Leetcode的問題可以用這個方法解決。dp解法我還不會。。。沒得到最優子結構,所以推不出遞歸式...emmm...但我的這個算法可以解決書上的這個問題,平均情況是很快的:我估計能在O(nlgn)以內,最好情況下為:O(n);但最壞情況下,比如:abcdef這類的就是O(n2)。

LPS(最長回文子序列)