1. 程式人生 > >夜深人靜寫演算法——最長公共子序列(動態規劃)

夜深人靜寫演算法——最長公共子序列(動態規劃)

一. 問題描述

給定兩個字串,求解這兩個字串的最長公共子序列(Longest Common Sequence)。比如字串1:BDCABA;字串2:ABCBDAB

則這兩個字串的最長公共子序列長度為4,最長公共子序列是:BCBA

二.尋找最優子結構,

用dp[i][j] 來表示第一個字串前 i 個字元和第二個字串前 j 個字元的最優解(最長公共子序列);

有兩種可能: (字元陣列 s1[n] 和字元陣列 s2[n] )

 1 .   s1[i] == s2[j] , dp[i][j] = dp[i-1][j- ]+1  : 如果s1[i] == s2[j]  那麼說明dp[i][j] 的最優解是dp[i-1][j-1]的最優解加一;

 2 .   s1[i] != s2[j]  , 如果  dp[i][j-1] > dp[i-1][j]  ,那麼dp[i][j] = dp[i][j-1] ; 如果dp[i-1][j] > d[i][j-1] ,那麼dp[i][j] = dp[i-1][j];

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define MAX 10
using namespace std;

int MaxL(string &str1,string &str2,int BT[MAX][MAX]){
	size_t size1 = str1.size();      //字串str1的長度
	size_t size2 = str2.size();      //字串str2的長度
	int **dp;
	 dp = new int*[size1];
	for(int i = 0; i < size1 ;i++)
	 	dp[i] = new int[size2];
	 	
	for(int i = 0; i < size1;i++)
		dp[i][0] = 0;                  //如果str1的長度為0,最長公共子序列為0
	for(int i = 0;i < size2;i++)
		dp[0][i] = 0;                  //如果str2的長度為0,最長公共子序列為0
	for(int i = 1; i < size1;i++)
		for(int j = 1; j < size2;j++){
			if(str1[j] == str2[i]){   //如果相等
				dp[i][j] = dp[i-1][j-1] + 1; BT[i][j] = 1; //BT陣列用來記錄對於字串的操作,用來尋找最優子序列
			}else if(dp[i-1][j] > dp[i][j-1]){     
				dp[i][j] = dp[i -1][j] ;     BT[i][j] = 2;
			}else{
				dp[i][j] = dp[i][j -1] ;	 BT[i][j] = 3;
			}
		}
} 

void Traceback(int i,int j,int BT[MAX][MAX],string &x){ //通過記錄
	if(i == 0 || j== 0)
		return;
	else{
		if(BT[i][j] == 1){            //操作1:如果str1的前i個字元和str2的前j個字元的最優解進行的操作是 1 的話,就輸出str1[i],然後在追溯到str1的前i-1個字元和str2的前j-1個字元的最優解
			Traceback(i-1,j-1,BT,x);cout<<x[i];
		}else if(BT[i][j] == 2)  
			Traceback(i-1,j,BT,x);   //操作2:最優解進行的操作是2,不輸出,直接追溯到str1的i-1個字元和str2的j個字元的最優解
		 else 
			Traceback(i,j-1,BT,x);   //操作3:最優解進行的操作是2,不輸出,直接追溯到str1的i個字元和str2的j-1個字元的最優解
	}
}

int main(){
 	string A = "ABCDEFGHTI";
	string B = "BCDAEFHGIT";
	int BT[10][10];
	MaxL(A,B,BT);
	cout<<"最長公共子序列為:"; 
	Traceback(10,10,BT,A);
}