除了背包问题和走金字塔问题,最长公共子序列也是常见动态规划例子,本篇文章简单分析该问题。
最长公共子序列问题形式如下:有两个字符串,求出最长公共子序列的长度。示例如:
字符串A: a s d h e j f
字符串B: a r s g k l g d
则字符串A和B的最长公共子序列为“asd”,最大公共子序列长度为3。最长公共子序列与最长公共子串不同的是:最长公共子序列是两个字符串中抽取出来的出现顺序相同、但不必连续的子串,最长公共子串是两个字符串中抽取出来的出现顺序相同、也要连续的子串。
与0-1问题的解法类似,此问题分析步骤如下:
1、用一个多维数组dp的形式来模拟计算过程,数组的初始值可以用0来填充。
2、定义状态。如dp[ i ][ j ],定义为字符串A[0...i]和字符串B[0...j]的最长公共子序列长度。关于dp[ i ][ j ] 的变化过程,可以分为两类:
1>当A[ i ]和B[ j ]相等时,则两个字符串需要公共减去共有序列,然后最长公共子序列数量+1,即dp[ i ][ j ] = dp[ i-1 ][ j-1 ] +1。
2>当A[ i ]和B[ j ]不相等时,就需要将A或B的字符串减去1个元素,然后取两者的最大值,即dp[ i ][ j ] = max(dp[ i-1 ][ j ],dp[ i ][ j-1 ])。
当最后填满数组dp时,dp[ A.length() ][ B.length() ]即为所求的最长公共子序列长度。
Java代码如下:
String str1 = "asdhejf";
String str2 = "arsgklgd";
//构建动态规划表
int[ ][ ] dp = new int[str1.length() + 1][str2.length() + 1];
//赋予动态规划表初始值
for (int i = 0; i <= str1.length(); i++) {
for (int j = 0; j <= str2.length(); j++) {
dp[ i ][ j ] = 0;
}
}
for (int i = 1; i <= str1.length(); i++) {
for (int j = 1; j <= str2.length(); j++) {
if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
//两个字符串中要比较的元素相等
dp[ i ][ j ] = dp[i - 1][j - 1] + 1;
} else {
//两个字符串中要比较的元素不相等,分别在两个字符串中减去一元素,取较大值
dp[ i ][ j ] = Math.max(dp[i - 1][j],dp[i][j - 1]);
}
}
}