第二个动态规划例题:
上一个动态规划例题是一维数组,比较简单,这次的是二维数组,相对比较难一些了。
例题:
动态规划解决最长公共子序列问题。
题目描述:求两字符串序列的最长公共字符子序列的长度。一个字符串的子序列,是指从该字符串中去掉任意多个字符后剩下的字符在不改变顺序的情况下组成的新字符串。最长公共子序列,是指多个字符串可具有的长度最大的公共的子序列。如字符串“abcdefsad”和字符串“abcdsad”,他们的最长公共子序列是“abcdsad”,长度为7。
首先,应该要将问题拆分成小问题。比如说上面的那个问题可以先拆分成“abcdefsad”和“abcdsa”,“abcdefsa”和“abcdsad”,“abcdefsa”和“abcdsa”的最大公共子串。仔细观察这几个小问题,不难发现状态方程(其实就是问题的表达式):martix[i][j],其中i表示第一个字符串的前i个字符,j表示第二个字符串的前j个字符,那么上面三个小问题就是martix[size1][size2 - 1];martix[size1 - 1][size2]和martix[size1 - 1][size2 - 1]。
那么,martix[size1][size2 ]等于多少呢? 看前面拆出来的3个小问题不难发现,分两种情况,当第一个字符串的size1位置和第二个字符串的size2位置字符相等的情况下,martix[size1][size2 ] = martix[size1 - 1][size2 - 1] + 1(为啥不用说了吧。。。 );如果字符不想等呢? 应该等于martix[size1 - 1][size2 ]和martix[size1][size2 - 1]里比较大的那个(不用比较martix[size1 - 1][size2 - 1 ] 了,因为在计算martix[size1 ][size2 - 1 ] 的时候,肯定已经计算过这个值了)。
举个比较简单的例子,比如说计算两个字符串“O...OX”和字符串“O...XO”的值,现在计算到画横线的部分了,即画横线之前的部分全都计算完成了。如果需要比对X和O的值,如果X和O相等,那么这两个字符串的最长公共子串是多少呢? 应该是字符串“O...O”和字符串“O...X”的最长公共子串+1;这个不难理解。 如果X和O不想等呢? 最长公共子串,应该是"O...OX"和“O...X”的最长公共子序列,或者“O...O“和“O...XO”的最长公共子序列,取大着。如果还不明白为什么,建议在一张白纸上多画画。。。
理解了思路,代码就很简单了:
/** * LCS: 动态规划解决最长公共子序列的问题 * @param str1 * @param str2 * @return * int 返回类型 */ private static int LCS(String str1, String str2) { //定义一个二维数组,其中martix[i][j]表示第一个字符串坐标i之前的子串和第二个字符串j之前的子串的最长公共自序列的长度 int[][] martix = new int[str1.length() + 1][str2.length() + 1]; // 填充二维数组 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)) { martix[i][j] = martix[i - 1][j - 1] + 1; } else { martix[i][j] = Math.max(martix[i][j - 1], martix[i - 1][j]); } } } return martix[str1.length()][str2.length()]; }