算法基础:动态规划(二)
- 学习笔记
- 2025-06-03
- 76热度
- 0评论
算法基础:动态规划(二)
关于动态规划,这里再举一个书上的经典例子。
求最长公共子序列(LCS)
问题描述:
令序列
给定两个序列
问题分析:
如果是匹配字符串问题的话,可以用经典KMP算法,可惜不是,求不得。如果使用暴力算法,穷举
1)刻画最长公共子序列问题的最优子结构。
设
(1)如果
(2)如果
(3)如果
2) 递归定义最优解的值
3) 代码演示
C++
x941
2
3
4
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+1
26 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
xxxxxxxxxx
501#!/usr/bin/env python
2# coding=utf-8
3
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] + 1
19 diagram[i][j][1] = 3
20 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] = 1
24 else:
25 diagram[i][j][0] = diagram[i][j-1][0]
26 diagram[i][j][1] = 2
27
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-=1
34 elif diagram[xi][yi][1] == 2:
35 yi-=1
36 elif diagram[xi][yi][1] == 3:
37 LCS.insert(0,x[xi-1])
38 xi-=1
39 yi-=1
40
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
这么看来动态规划算法的有两个比较难的点:写出最优解结构的递归式和找到合适的回溯算法。