http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2399
题目大意:
给定一个由小写字母组成的字符串,删除其中0个或多个字符,使得剩下的字母(顺序不变)组成一个尽量长的回文串。如果有多解,输出字典序最小的解。
思路:
参考别人思路的。。
学到的有:
把一个字符串逆序后和原字符串进最长公共子序列,可以计算出它的最长回文串长度。
这题还要求输出字典序最小的解。
回忆一下LCS过程,如果A[I]==B[j]那么dp[i][j]=dp[i-1][j-1]+1,否则dp[i][j]=max(dp[i-1,j],dp[i][j-1])
这题则需要在dp过程中记录当前串。至于字典序的判断,如果A[I]!=B[j]并且dp[i-1,j]==dp[i][j-1]时,判断(i-1,j)和(i,j-1)记录的字符串哪个字典序小即可。
需要注意的是LCS不一定为回文串。
例如:
kfclbckibbibjccbej
jebccjbibbikcblcfk
bcibbibc是他们的LCS,但是却不是回文串。
但我们可以根据前半段推出后面的。
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1024;
char s[MAXN],a[MAXN];
int dp[MAXN][MAXN];
string ans[MAXN][MAXN];
int main()
{
while(~scanf("%s",s+1))
{
int L=strlen(s+1);
for(int i=0;i<L;i++)
{
dp[0][i]=0;
ans[0][i]="";
}
for(int i=L,j=1;i>=1;i--,j++)
a[j]=s[i];
//这样,问题转化为求s和a的LCS
for(int i=1;i<=L;i++)
{
for(int j=1;j<=L;j++)
{
if(a[j]==s[i])
{
dp[i][j]=dp[i-1][j-1]+1;
ans[i][j]=ans[i-1][j-1]+s[i];
}
else
{
if(dp[i-1][j] > dp[i][j-1])
{
dp[i][j]=dp[i-1][j];
ans[i][j]=ans[i-1][j];
}
else if(dp[i-1][j] < dp[i][j-1])
{
dp[i][j]=dp[i][j-1];
ans[i][j]=ans[i][j-1];
}
else
{
dp[i][j]=dp[i][j-1];
ans[i][j]=min(ans[i-1][j],ans[i][j-1]);
}
}
}
}
string &res=ans[L][L];
int len=res.size();
len>>=1;
// cout<<res<<endl;
for(int i=0;i<len;i++)
cout<<res[i];
int i;
if(res.size() &1)
i=len;
else
i=len-1;
for(;i>=0;i--)
cout<<res[i];
cout<<endl;
}
}