算法基础:动态规划(二)
算法基础:动态规划(二)
关于动态规划,这里再举一个书上的经典例子。
求最长公共子序列(LCS)
问题描述:
令序列
给定两个序列
问题分析:
如果是匹配字符串问题的话,可以用经典KMP算法,可惜不是,求不得。如果使用暴力算法,穷举
1)刻画最长公共子序列问题的最优子结构。
设
(1)如果
(2)如果
(3)如果
2) 递归定义最优解的值
3) 代码演示
C++
x941234
5using std::cout;6using std::endl;7using std::string;8using std::vector;9
10
11
12//算法主体13void Match(string x,string y,int lenx,int leny){14
15 //建立图表16 vector<vector<std::pair<int,int>>> diagram(lenx+1);17 for(auto & v:diagram){18 v.resize(leny+1,{0,0}); // 初始化19 }20 21
22 // pair的第二位用于表示当前字符是否属于X和Y的公共子序列23 // 1,2分别表示“当前X的字符不在公共子序列中,且当前X_i与Y_j的LCS和X_(i-1)与Y_j的LCS一样",24 // ”当前Y的字符不在公共子序列中,且当前X_i与Y_j的LCS和X_i与Y_(j-1)的LCS一样"25 // 3表示当前X与Y字符相等,且X_i与Y_j的LCS等于X_(i-1)与Y_(j-1)的LCS+126 for(int i = 1; i < lenx+1 ; ++i){27 for(int j = 1; j < leny+1 ; ++j){28 if(x[i-1] == y[j-1]){29 diagram[i][j].first = diagram[i-1][j-1].first+1;30 diagram[i][j].second = 3;31 }32 else{33 if(diagram[i-1][j].first > diagram[i][j-1].first){34 diagram[i][j].first = diagram[i-1][j].first;35 diagram[i][j].second = 1;36 } 37 else{38 diagram[i][j].first = diagram[i][j-1].first;39 diagram[i][j].second = 2;40 }41 }42 }43 }44 45 // 回溯得到最长公共序列 46 string lcs;47 int a = lenx;48 int b = leny;49 while(a > 0 && b > 0){50 switch(diagram[a][b].second)51 {52 case 1:53 --a;54 break;55 case 2:56 --b;57 break;58 case 3:59 lcs+=x[a-1];60 --a;61 --b;62 break;63 }64 }65
66 // 打印结果67 for(int i = 1; i < lenx+1; ++i){68 for(int j = 1 ; j < leny+1 ; ++j){69 printf("{%d,%d} ",diagram[i][j].first,diagram[i][j].second);70 }71 printf("\n");72 } 73 cout << "lcs: ";74 std::reverse(lcs.begin(),lcs.end());75 for(const auto & it : lcs){76 cout << it;77 }78 cout << endl;79 80 cout << "size of lcs: " << diagram[lenx][leny].first << endl;81
82}83
84
85
86int main(){87 string X = "BFCDCVB";88 string Y = "EFBFUCB";89
90
91 Match(X,Y,X.length(),Y.length());92 return 0;93}94
Python
xxxxxxxxxx501#!/usr/bin/env python2# coding=utf-83
4
5def Match(x,y):6 7 # 初始化图8 diagram = []9 for i in range(len(x)+1):10 diagram.append([]); 11 for j in range(len(y)+1):12 diagram[i].append([0,0])13 14 # 填表15 for i in range(1,len(x)+1):16 for j in range(1,len(y)+1):17 if x[i-1] == y[j-1] :18 diagram[i][j][0] = diagram[i-1][j-1][0] + 119 diagram[i][j][1] = 320 else:21 if diagram[i-1][j] > diagram[i][j-1]:22 diagram[i][j][0] = diagram[i-1][j][0]23 diagram[i][j][1] = 124 else:25 diagram[i][j][0] = diagram[i][j-1][0]26 diagram[i][j][1] = 227
28 # 回溯29 LCS = []30 xi,yi = len(x),len(y) 31 while (xi > 0) and (yi > 0):32 if diagram[xi][yi][1] == 1:33 xi-=134 elif diagram[xi][yi][1] == 2:35 yi-=136 elif diagram[xi][yi][1] == 3:37 LCS.insert(0,x[xi-1])38 xi-=139 yi-=140 41 # 打印结果42 print("LCS: {}".format("".join(LCS)))43 print(f"length: {diagram[-1][-1][0]}")44
45if __name__ == "__main__":46 X = "BFCDCVB"47 Y = "EFBFUCB"48
49 Match(X,Y)50
这么看来动态规划算法的有两个比较难的点:写出最优解结构的递归式和找到合适的回溯算法。