【模板】动态规划之基本思想、最长公共子序列LCS问题 (Coincidence题解)

DP基本思想

将求解的问题分解成若干子问题,先求解子问题,再从这些子问题得到原问题的解。

DP的两个基本要素(重要性质):

1. 最优子结构性质。即问题的最优解包含了其子问题的最优解。

2. 重叠子问题性质。有的子问题可能会被计算多次。因此采用备忘录方法避免对相同子问题的重复求解。

DP编程关键

状态转移方程、边界条件

最长公共子序列(LCS)

Description: 
给两个长度为n的全排列,求其最长公共子序列长度。 
Input: 
第一行是一个正整数N,表示全排列长度。 
第二行有n个整数,保证是一个n的全排列。 
第三行有n个整数,保证是一个n的全排列。 
Output: 
输出第一行有一个整数,表示两数组最长公共子序列长度。 
Sample Input: 

1 3 2 4 5 
5 2 3 1 4 
Sample Output: 

 

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX = 1e3+7;
int dp[MAX][MAX]; 
int str1[MAX],str2[MAX];
int main(void){
	int n;
	while(cin>>n){
		for(int i=1;i<=n;i++) cin>>str1[i];
		for(int i=1;i<=n;i++) cin>>str2[i];
		memset(dp,0,sizeof(dp));
		for(int i=0;i<=n;i++){
			for(int j=0;j<=n;j++){
				if(i==0||j==0) dp[i][j]=0;
				else if(str1[i]==str2[j]) dp[i][j]=dp[i-1][j-1]+1;
				else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			}
		}
		cout<<dp[n][n]<<endl;
	}
	return 0;

}

Coincidence

题目描述

Find a longest common subsequence of two strings.

输入描述

First and second line of each input case contain two strings of lowercase character a…z. There are no spaces before, inside or after the strings. Lengths of strings do not exceed 100.

输出描述

For each case, output k – the length of a longest common subsequence in one line.

输入

abcd
cxbydz

输出

2

和上面的题其实完全一样。

#include<iostream>
#include<cstring>
using namespace std;
const int MAX =105;
int dp[MAX][MAX];
int max(int a,int b){
	return a>b?a:b; 
}
int main(void)
{
	char a[105],b[105];
	while(scanf("%s%s",&a,&b)!=EOF){
		int a_len = strlen(a);
		int b_len = strlen(b);
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=a_len;i++){
			for(int j=1;j<=b_len;j++){
				if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1;
				else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			} 
		}
		cout<<dp[a_len][b_len]<<endl;
	}
	return 0;
}
 

如果要求找出这个最长公共子序列是什么呢?考虑记录每一次的转移方向。

思路:用flag[][]记住当前状态是从什么方向转移而来的,然后从flag[n][n]开始进行回溯。如果flag[i][j]==0,则说明此时str1[i]==str2[j],状态dp[i][j]是从dp[i-1][j-1]转移而来的,此时打印出这个字符。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX = 1e3+7;
int dp[MAX][MAX]; 
int flag[MAX][MAX];
int str1[MAX],str2[MAX];
void printSeq(int i,int j)
{
	if(i==0||j==0) return;
	if(flag[i][j]==0){	//该位置从左上方转移而来,说明出现了str1[i]==str2[j] 
		cout<<str1[i]<<" ";
		printSeq(i-1,j-1);
	}
	if(flag[i][j]==1){	//从上方转移而来 
		printSeq(i-1,j); 
	}
	if(flag[i][j]==-1){//从左方转移而来 
		printSeq(i,j-1); 
	}
 } 
int main(void){
	int n;
	while(cin>>n){
		for(int i=1;i<=n;i++) cin>>str1[i];
		for(int i=1;i<=n;i++) cin>>str2[i];
		memset(dp,0,sizeof(dp));
		memset(flag,0,sizeof(flag)); 
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(str1[i]==str2[j]){
					dp[i][j]=dp[i-1][j-1]+1;
					flag[i][j]=0;//表示从左上方转移而来 
				} 
				else
				{
					if(dp[i-1][j]>dp[i][j-1]){
						dp[i][j]=dp[i-1][j];
						flag[i][j]=1;//表示从上方转移而来 
					} 
					else{
						dp[i][j]=dp[i][j-1];
						flag[i][j]=-1;//表示从左方转移而来 
					}
				}
				//dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			}
		}
		cout<<dp[n][n]<<endl;
		printSeq(n,n);//倒序打印 
	}
	return 0;
}

动态规划问题总结

【模板】动态规划之背包问题(01背包、多重背包、完全背包) (邮票、Piggy-bank题解)

【模板】动态规划之最长上升子序列LIS问题、概率DP问题 (烟花题解)

【模板】动态规划之基本思想、最长公共子序列LCS问题 (Coincidence题解)

【模板】动态规划之最大字段和、最大子矩阵问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值