求两个字符串的最长公共子序列——程序设计艺术与方法实验四 动态规划算法的实现

求两个字符串的最长公共子序列——程序设计艺术与方法实验四 动态规划算法的实现

(1) 求两个字符串的最长公共子序列。 - 151 - X 的一个子序列是相应于 X 下标序列{1, 2, …, m}的一个子序列,求解两个序列的所有子序列中长度最大的,例如输入:pear, peach 输出:pea。
在这里插入图片描述
步骤:

  1. 确定子问题
  2. 确定状态转移方程
    res[i][j]表示text1截止到第i个字符串,text2截止到第j个字符串的最长公共子串
    text1[i]=text2[j],res[i][j]=原结果 + 1
    text1[i]!=text2[j],res[i][j]=原结果
    原结果? res[i-1][j],res[i-1][j-1] √,res[i][j-1]
    text1[i] = text2[j],res[i][j] = res[i-1][j-1] + 1
    text1[i] != text2[j],res[i][j] = max{res[i-1][j],res[i][j-1]}
    因此,此题在计算出一个子问题的答案后, 将其存储在一个表中。此后调用将检查表。
    我们先通过状态转移方程填表,并返回表的最右下角元素(最长公共子序列长度)
    之后我们根据递归公式构建了上表,我们将从最后一个元素res[m][n]倒推出text1和text2的LCS。
    如果遇到text 1[i] == text 2[j],记录此时的公共值,此时的值来自于res[i-1][j-1]
    如果遇到text 1[i] != text 2[j] ,此时的值来自于max{res[i-1][j] ,res[i][j-1] }即他两的最大值。
    如果遇到text 1[i] != text 2[j] ,且res[i-1][j] = res[i][j-1] 这种存在分支的情况,这里请都选择一个方向(之后遇到这样的情况,也选择相同的方向)。两种方向都选进行递归。

代码:

#include<bits/stdc++.h>
using namespace std;
/*
给定两个字符串text1和text2,返回这两个字符串的最长公共子序列的长度
一个字符串的子序列是指这样一个新的字符串:
它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串
eg. text1 = "abcbcba", text2 = "abcba" 公共子串abcba
步骤:
1.确定子问题
2.确定状态转移方程
res[i][j]表示text1截止到第i个字符串,text2截止到第j个字符串的最长公共子串
text1[i]=text2[j],res[i][j]=原结果 + 1
text1[i]!=text2[j],res[i][j]=原结果
原结果? res[i-1][j],res[i-1][j-1] √,res[i][j-1]
text1[i] = text2[j],res[i][j] = res[i-1][j-1] + 1
text1[i] != text2[j],res[i][j] = max{res[i-1][j],res[i][j-1]}
*/
string test1;
string test2;
vector<vector<int> > res; // 动态规划表
set<string> lcs;      // set保存所有的LCS
int lcs_length(int m, int n){
	// 表的大小为(m+1)*(n+1)
	res = vector<vector<int> >(m + 1, vector<int>(n + 1));
	for (int i = 0; i < m + 1; ++i){
		for (int j = 0; j < n + 1; ++j){
			// 第一行和第一列置0
			if (i == 0 || j == 0)
				res[i][j] = 0;
			else if (test1[i - 1] == test2[j - 1])
				res[i][j] = res[i - 1][j - 1] + 1;
			else
				res[i][j] = max(res[i - 1][j], res[i][j - 1]);
		}
	}
	return res[m][n];
}
void lcs_print(int i, int j, string lcs_str){
	while (i > 0 && j > 0){
		if (test1[i - 1] == test2[j - 1]){
			lcs_str.push_back(test1[i - 1]);
			i--;
			j--;
		}
		else{
			if (res[i - 1][j] > res[i][j - 1])
				i--;
			else if (res[i - 1][j] < res[i][j - 1])
				j--;
			else{
				lcs_print(i - 1, j, lcs_str);
				lcs_print(i, j - 1, lcs_str);
				return;
			}
		}
	}
	reverse(lcs_str.begin(), lcs_str.end());
	lcs.insert(lcs_str);
}
int main(){
	while(cin >> test1){
		cin>> test2;
		int m = test1.length();
		int n = test2.length();
		int length = lcs_length(m, n);
		cout << "最长公共子串的长度是:" << length << endl;
		string str;
		lcs_print(m, n, str);
		set<string>::iterator it = lcs.begin();
		cout << "最长公共子串是:"<<endl ;
		for (; it != lcs.end(); it++)
			cout << *it << endl;
		lcs.clear();
		res.clear();
	}
	return 0;
}

运行结果:
在这里插入图片描述

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值