分治思想实际应用的一种方法-动态规划
在应用分治思想将大问题分解成若干个小问题后,同样的问题可能被反复计算这就造成了效率上的负担,以斐波那契数列为例直接的递归操作的时间效率是指数级,如果采用自低而上的策略则为O(n),这里的自低而上就是动态规划。
动态规划的特点
- 分析一个最优解决方案的结构
- 用递归的方法定义最优解决方案
- 由低而上定义一个最优结构
最长公共子序列
题目:假设两个字母序列S1= {A…},S2 = {B…}
寻找两个字符串最长的公共子串,子串的要求如下:
1,子串里的字符出现的顺序必须相同。
2,子串可能不唯一只需找出其中一个。
3,子串的字符在原序列里可以不是连续的。以上。
策略一:蛮力法
找到一个字符串所有的子串,检查是否存在于另外一个子串,从而选出最长的公共子串。
时间效率:假设字符串的长度是m,每个子串检查的效率为O(n),总共有2n个子串,所以时间效率是指数级;
此方法不可取。
策略二:动态规划
从动态规划思想出发,假设S1= {1…n},S2 = {1…m}(m,n不一定相等,C= {1…i}是最长公共子串。
如果S1[n] = S2[m],则有S1[n] = S2[m] = C[i];所以 C[1…i] = C[1…i-1] +1;
如果S1[n] != S2[m],分三种情况讨论
- S1[n] = C[i],S2[m] != C[i], 所以C是S2={1…m-1}的子串。
- S1[n] != C[i],S2[m] = C[i], 所以C是S1={1…n-1}的子串。
- S1[n] != C[i],S2[m] != C[i], 所以C是S2={1…m-1}与S1={1…n-1}的子串。
所以C{1…i} = Max最长子串(S1={1…n-1},S2={1…m})或者是Max最长子串(S1={1…n},S2={1…m-1});
换句话说就是最长公共子串的子串一定是原字符串的某子串的最长公共子串。
如果设LCS(S1,S2,i,j)表示S1[1…i]和S2[1…j]的最长公共子序列的长度,则有:
if(S1[i,j] ==S2[i,j])
C[i,j] = LCS(S1,S2,i-1,j-1);
else
C[i,j] = Max(LCS(S1,S2,i-1,j),LCS(S1,S2,i,j-1));
时间效率分析
上面伪代码给出的递归方法的时间效率是2m+n指数级的复杂度显然不符合我们的要求,这里就要用到动态规划,潜在的不同最长子串的数量为mn,mn的复杂度明显优于2m+n。
c++代码示例
int m = S1.length();
int n = S2.length();
int ** C1 = new int *[m+1];
for (int i = 0; i <= m; i++)
{
C1[i] = new int[n+1];
}
for (int i = 0; i <=m; i++)
{
for (int j = 0; j <=n; j++)
{
C1[i][j] = 0;
cout << C1[i][j];
}
cout << endl;
}
for (int i = 1; i <=m; i++)
{
for (int j = 1; j <=n; j++)
{
if (S1[i-1] == S2[j-1])
{
C1[i][j] = C1[i - 1][j - 1] + 1;
}
else if (C1[i - 1][j] <= C1[i][j - 1])
{
C1[i][j] = C1[i][j - 1];
}
else
{
C1[i][j] = C1[i - 1][j];
}
}
}
int Source = C1[m][n];
for (int i = 0; i <= m; i++)
{
delete []C1[i];
}
delete[]C1;
return Source;