动态规划和求解最长公共子序列介绍
动态规划是一种在数学和计算机科学中使用的,用于求解包含重叠子问题的最优化问题的方法。其基本思想是,将原问题分解为相似的子问题,在求解的过程中通过子问题的解求出原问题的解。动态规划的思想是多种算法的基础,被广泛应用于计算机科学和工程领域。比较著名的应用实例有:求解最短路径问题,背包问题,项目管理,网络流优化等。
概述
动态规划只能应用于有最优子结构的问题。最优子结构的意思是局部最优解能决定全局最优解(对有些问题这个要求并不能完全满足,故有时需要引入一定的近似)。简单地说,问题能够分解成子问题来解决。
步骤
- 最优子结构性质。如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。
- 子问题重叠性质。子问题重叠性质是指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的效率。
实例
斐波那契数列(Fibonacci polynomial)
计算斐波那契数列(Fibonacci polynomial)的一个最基础的算法是,直接按照定义计算:
function fib(n) if n = 0 or n = 1 return 1 return fib(n − 1) + fib(n − 2)
当n=5时,fib(5)的计算过程如下:
fib(5)
fib(4) + fib(3)
(fib(3) + fib(2)) + (fib(2) + fib(1))
((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
(((fib(1) + fib(0)) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
由上面可以看出,这种算法对于相似的子问题进行了重复的计算,因此不是一种高效的算法。实际上,该算法的运算时间是指数级增长的。 改进的方法是,我们可以通过保存已经算出的子问题的解来避免重复计算:
array map [0...n] = { 0 => 0, 1 => 1 } fib( n ) if ( map( n ) is cached ) return map( n ) return map( n ) = fib( n - 1 ) + fib( n - 2 )
将前n个已经算出的前n个数保存在数组map中,这样在后面的计算中可以直接易用前面的结果,从而避免了重复计算。算法的运算时间变为O(n)
最长公共子序列
问题描述
最长公共子序列是一个十分实用的问题,它可以描述两段文字之间的“相似度”,即它们的雷同程度,从而能够用来辨别抄袭。对一段文字进行修改之后,计算改动前后文字的最长公共子序列,将除此子序列外的部分提取出来,这种方法判断修改的部分,往往十分准确。
最长公共子序列也称作最长公共子串(不要求连续),英文缩写为LCS(Longest Common Subsequence)。其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。
例如,若x=<A,B,C,B,D,A,B>和y=<B,D,C,A,B,A>,则序列<B,C,A>是x和y的一个公共子序列,序列<B,C,B,A>也是x和y的一个公 共子序列。而且,后者是x和y的一个最长公共子序列,因为x和y没有长度大于4的公共子序列。
输入
输入数据有T组测试数据。测试数据的数目 (T)在输入的第一行给出。每组测试数据有两行:每行有一个字符串,每个字符串的长度都不超过20。
输出
每个用例,用一行输出其最大公共子序列的长度,如果没有公共子序列,则输出0。
样例输入
2
abceef
1235896
ABCBDAB
BDCABA
样例输出
0
4
代码:
1 #include <iostream>
2 using namespace std;
3 void fun(int M[][100],char a[100],char b[100],int m,int n)
4 {
5 int i,j;
6 for(i=1;i<=m;i++)M[i][0]=0;
7 for(j=1;j<=n;j++)M[0][j]=0;
8 for(i=1;i<=m;i++)
9 for(j=1;j<=n;j++)
10 {
11 if(a[i-1]==b[j-1]){M[i][j]=M[i-1][j-1]+1;}
12 else if(M[i-1][j]>=M[i][j-1]){M[i][j]=M[i-1][j];}
13 else {M[i][j]=M[i][j-1];}
14 }
15 }
16 int main()
17 {
18 char a[100],b[100];
19 int c[100][100];
20 int m,n,k;
21 cin>>k;
22 while(k!=0)
23 {
24 cin>>a;
25 m=strlen(a);
26 cin>>b;
27 n=strlen(b);
28 fun(c,a,b,m,n);
29 cout<<c[m][n]<<endl;
30 k--;
31 }
32 return 0;
33 }
3 void fun(int M[][100],char a[100],char b[100],int m,int n)
4 {
5 int i,j;
6 for(i=1;i<=m;i++)M[i][0]=0;
7 for(j=1;j<=n;j++)M[0][j]=0;
8 for(i=1;i<=m;i++)
9 for(j=1;j<=n;j++)
10 {
11 if(a[i-1]==b[j-1]){M[i][j]=M[i-1][j-1]+1;}
12 else if(M[i-1][j]>=M[i][j-1]){M[i][j]=M[i-1][j];}
13 else {M[i][j]=M[i][j-1];}
14 }
15 }
16 int main()
17 {
18 char a[100],b[100];
19 int c[100][100];
20 int m,n,k;
21 cin>>k;
22 while(k!=0)
23 {
24 cin>>a;
25 m=strlen(a);
26 cin>>b;
27 n=strlen(b);
28 fun(c,a,b,m,n);
29 cout<<c[m][n]<<endl;
30 k--;
31 }
32 return 0;
33 }