【动态规划C语言】求解最长公共子序列问题(并输出该序列)原理+代码

子序列:给定一个序列X=<x1,x2,x3,x4...,xm>,另一个序列Z=<z1,z2,z3,z4...,zk>,若存在一个严格递增的X的下标序列<i1,i2,i3,...,ik>对所有的1,2,3,...,k,都满足x(ik)=zk,则称Z是X的子序列(不一定要连续但顺序必须相同)

补充:字符子串和字符子序列的区别

字符字串指的是字符串中连续的n个字符;如palindrome中,pa,alind,drome等都属于它的字串

而字符子序列指的是字符串中不一定连续但先后顺序一致的n个字符;如palindrome中,plind,lime属于它的子序列,而mod,rope则不是,因为它们与字符串的字符顺序不一致。

那么如何由dp求出LCS呢?

(1)当元素值等于上方相邻元素值时i-1,即dp[i][j]=dp[i-1][j]

(2)当元素值等于左方相邻元素值时j-1,即dp[i][j]=dp[i][j-1]

(3)若元素值与上方、左方元素均不相等时,说明字符相同了,将a[i]添加到数组中

LCS算法由于使用了两重循环,所以对于长度分别为m、n的序列,其时间复杂度和空间复杂度都是O(mn)

图解如下: 

完整代码如下:

#include <stdio.h> 
#include <string.h>
using namespace std;
const int MAXSIZE=1001;
char a[MAXSIZE];
char b[MAXSIZE];
int c[MAXSIZE][MAXSIZE];
char dp[MAXSIZE][MAXSIZE];

void LCS(int m,int n){
    //先初始化
	for(int i=0;i<=m;i++) c[i][0]=0;
	for(int j=0;j<=n;j++) c[0][j]=0;
    //两重循环处理a、b的所有字符 
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			if(a[i]==b[j]){
				c[i][j]=c[i-1][j-1]+1;
				dp[i][j]='W';        //此时有相同的字符 
			}else if(c[i-1][j]>=c[i][j-1]){
					c[i][j]=c[i-1][j];  
					dp[i][j]='U';    //此时没有相同的字符
	     	}else{
	     	    c[i][j]=c[i][j-1];
				dp[i][j]='L';        //此时也没有相同的字符 
				}
			}
		}
}

void Print(int m,int n){
	int i=m;
	int j=n;
	if(i==0||j==0) return;
	if(dp[i][j]=='W'){
		Print(i-1,j-1);    //相同,沿对角线走,且需要输出 
		printf("%c",a[i]);
	}else if(dp[i][j]=='U'){ 
		Print(i-1,j);      //向上走 
	}else Print(i,j-1);    //向左走
}

int main(){
	scanf("%s %s",a+1,b+1); 
	int m=strlen(a+1);
	int n=strlen(b+1);
	LCS(m,n);
	Print(m,n);
}

猜你喜欢

转载自blog.csdn.net/KK_1657654189/article/details/120846342