UVA 11404 - Palindromic Subsequence(LCS和DP两种做法)

ASubsequence is a sequence obtained by deleting zero or more characters in astring. A Palindrome is a string which when read from left to right, reads sameas when read from right to left. Given a string, find the longest palindromicsubsequence. If there are many answers to it, print the one that comes lexicographicallyearliest.

Constraints

• Maximum length of string is 1000.

• Each string has characters `a' to `z' only.

Input

Inputconsists of several strings, each in a separate line. Input is terminated byEOF.

Output

For eachline in the input, print the output in a single line.

Sample Input

aabbaabb

computer

abzla

samhita

Sample Output

aabbaa

c

aba

aha

 

题目大意:题意是说找一个给定字符串的最大回文子序列,但这个子序列可以不是连续的,对于多种同样长度的回文子串,取字典序最小的。

根据字符串S0的长度可以采用LCS算法,找出字符串S0和其逆字符串S1的最长公共子序列,同时加一个判断就是对于相同长度的公共子串选取字典序最小的。但是还有一种特殊情况,由于LCS过程中是按照字典序排列的,本题要找的是最大回文子串,LCS并不一定能找到回文序列,借用别的例子:

kfclbckibbibjccbej 

jebccjbibbikcblcfk

按字典序LCS得到为bcibbibc,并不是回文串,但是可以得到其与最长回文子串的长度应该相等,并且前一半与回文子串相同,因此可以根据前一半回文串得出后一半(主要串长度的奇偶性)。LCS做法代码如下:

 

#include<bits/stdc++.h>
using namespace std;
string dp[1005][1005];
string s0;
string lcs()
{
	string s1(s0.rbegin(),s0.rend());//逆串
	int i,j,len;
	len=s0.length();
	for(i=0; i<1005; i++)
	{
		dp[0][i]="";
		dp[i][0]="";
	}
	for(i=1; i<=len; i++)
	{
		for(j=1; j<=len; j++)
		{
			if(s0[i-1]==s1[j-1])
			{
				dp[i][j]=dp[i-1][j-1]+s0[i-1];
			}
			else
			{
				if(dp[i-1][j].length()==dp[i][j-1].length())//字典序
				{
					dp[i][j]=min(dp[i-1][j],dp[i][j-1]);
				}
				else
				{
					if(dp[i-1][j]<dp[i][j-1])
					{
						dp[i][j]=dp[i][j-1];
					}
					else
					{
						dp[i][j]=dp[i-1][j];
					}
				}
			}
		}
	}
	return dp[len][len];
}

int main()
{
	int i,len;
	string ans;
	while(cin>>s0)
	{
		ans=lcs();
		len=ans.length();
		for(i = 0; i < len/2; i++)//输出  注意奇偶
			cout << ans[i];
		if(len & 1)
		{
			for(i = len/2; i >= 0; i--)
				cout << ans[i];
		}
		else
		{
			for(i =len/2-1; i >= 0; i--)
				cout << ans[i];
		}
		cout<<endl;
	}
	return 0;
}

练习赛时候做做到题不知道怎么想的,觉得LCS算法不行,于是想了个dp的算法,用book[i][j]表示从S[i]到S[j]的最长回文子串的一半,状态转移方程式book[i][j]=max(book[i+1][j],s[i]+book[i+1][k]),k为从j开始向前查找得到的第一个与S[i]相等的字符的位置,但这样做要判断回文串的长度是奇数还是偶数,还要判断字典序,就比较麻烦,不过不会出现LCS的那种奇葩的情况。这个做法代码如下:

 

#include<bits/stdc++.h>
using namespace std;
struct str
{
	string s;
	bool flag;//判断回文串长度是奇数还是偶数
};
str s;
str book[1005][1005];
int l;	
str dp(int p,int q)
{
	if(book[p][q].s!="")
	{
		return book[p][q];
		
	}
	int i,j,k;
	str s0,s1,kong,tmp;
	kong.flag=0;
	kong.s="";
	s0.s+=s.s[p];
	if(p-q==1)
	{
		kong.flag=0;
		return kong;
	}
	if(p-q==2)
	{
		kong.flag=1;
		return kong;
	}
		
	for(i=q;i>=p;i--)
	{
		if(s.s[i]==s.s[p])
		{
			tmp=dp(p+1,i-1);
			s0.s+=tmp.s;
			s0.flag=tmp.flag;
			s1=dp(p+1,q);
			if(s0.s.length()<s1.s.length())
			{
				book[p][q] = s1;
				return s1;
			}
			else
			{
				if(s0.s.length()>s1.s.length())
				{
					if(p==i)
						s0.flag=1;
					book[p][q] = s0;
					return s0;
				}
				else
				{
					if(s0.flag==1&&s1.flag==0)
					{
						book[p][q] = s1;
						return s1;
					}
					
					if(s0.flag==0&&s1.flag==1)
					{
						if(p==i)
							s0.flag=1;
						book[p][q] = s0;
						return s0;
					}
					
					if(s0.s>s1.s)
					{
						book[p][q] = s1;
						return s1;
					}
					else
					{
						if(p==i)
							s0.flag=1;
						book[p][q] = s0;
						return s0;
					}
				}
			}
		}
	}
}
int main()
{
	str s0;
	s0.flag=0;
	int i,j;
	while(cin>>s.s)
	{
		l=s.s.length();
		for(i=0;i<l;i++)
		{
			for(j=i;j<l;j++)
			{
				book[i][j].s="";
				book[i][j].flag=0;
			}
		}
		dp(0,l-1);
		s0=book[0][l-1];
		cout<<s0.s;
		if(s0.flag==1)
		{
			i=s0.s.length()-2;
		}
		else
		{
			i=s0.s.length()-1;
		}
		for(;i>=0;i--)
		{
			cout<<s0.s[i];
		}
		cout<<endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/outp0st/article/details/73693007