POJ 1934 Trip DP

题意:求最大公共子序列,并按字典序输出最大子序列。
题解:题目的难点在于输出。看了http://www.cppblog.com/varg-vikernes/archive/2010/09/27/127866.html
1.用 pos1[ i ] [ j ] 代表字符 'a'+j 在 s1 中第 i 个字符前最后出现的位置 ( 即 1 -- i 这个范围内,最后出现的位置 )。
 若字符 'a' + j 不存在,那么pos1 [ i ] [ j ] = 0;
pos2 同理。
2.枚举最长公共子序列的每一个字符。假设最长公共子序列的长度为 cnt, s1 长度为 l1, s2 长度为 l2。 我们先确定第 cnt 个字符, dfs ( l1, l2, cnt )。
它可能是 a, b, c, ····,z  的其中之一。假设是 a, 那么需要找出 a 在 s1 及 a在 s2 中最后出现的位置 pos1 [ l1 ] [ 0 ], pos2 [ l2 ] [ 0 ],  令 p1 = pos1[l1][0], p2 = pos2[l2][0]。 若 dp[p1][p2] == cnt ,那么 ans [ cnt ] = a,
在此基础上枚举第 cnt - 1 个字符, dfs ( p1 - 1, p2 - 1, cnt - 1 )。·····最终找到第 1 个字符,结束。

#include <queue>
#include <iostream>
using namespace std;

#define N 85

char s1[N], s2[N];
int dp[N][N], pos1[N][30], pos2[N][30];
struct item { char str[N]; } com;

bool operator> ( const item &a, const item &b )
{
	if (  strcmp  ( a.str, b.str ) > 0 )
		return true;
	return false;
}


priority_queue<item, vector<item>, greater<item> > que;

int max ( int a, int b )
{
	return a > b ? a : b;
}

void dfs ( int l1, int l2, int c )
{	
    	
	if ( c < 1 )
	{
		que.push ( com );
		return;
	}
	if ( l1 < 1 || l2 < 1 ) return;

	for ( int i = 0; i < 26; i++ )
	{
		int t1 = pos1[l1][i];  /* pos1[l1][i] 代表字符 'a'+i 在 s1 中最后出现的位置 */
		int t2 = pos2[l2][i];  /* pos2[l2][i] 代表字符 'a'+i 在 s2 中最后出现的位置 */
		if ( dp[t1][t2] == c )  
		{
			com.str[c-1] = 'a' + i;
			dfs ( t1 - 1, t2 - 1, c-1 );
		}
	}
}

int main()
{
	int i, j, len1, len2;
    scanf("%s%s",s1+1, s2+1);
   
	memset(dp,0,sizeof(dp));
	memset(pos1,0,sizeof(pos1));
	memset(pos2,0,sizeof(pos2));
	
	len1 = strlen(s1+1);
	len2 = strlen(s2+1);
	
	for ( i = 1; i <= len1; i++ ) /* 先求出最长子序列的长度 */
	{
		for ( j = 1; j <= len2; j++ )
		{
			if ( s1[i] == s2[j] )
				dp[i][j] = dp[i-1][j-1] + 1;
			else
				dp[i][j] = max ( dp[i-1][j], dp[i][j-1] );
		}
	}
	
	for ( i = 1; i <= len1; i++ )
	{
		for ( j = 0; j < 26; j++ )
		{
			if ( s1[i] == 'a' + j )
				pos1[i][j] = i;
			else
				pos1[i][j] = pos1[i-1][j];
		}
	}
	
	for ( i = 1; i <= len2; i++ )
	{
		for ( j = 0; j < 26; j++ )
		{
			if ( s2[i] == 'a' + j )
				pos2[i][j] = i;
			else
				pos2[i][j] = pos2[i-1][j];
		}
	}
	
	com.str[dp[len1][len2]] = '\0'; /* 先将子序列的末尾置为 '\0' */
	dfs ( len1, len2, dp[len1][len2] );

	while ( ! que.empty () )
	{
		printf("%s\n", que.top().str);
		que.pop();
	}	
	return 0;
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值