最长公共子序列问题
给定两个字符串str1和str2,返回两个字符串的最长公共子序列。
str1=“1A2C3D4B56”,str2=“B1D23CA45B6A”。
“123456”或者“12C4B6”都是最长公共子序列,返回哪一个都行。
思路
dp[i][j]的含义是str1[0..i]与str2[0..j]的最长公共子序列的长度。
从左到右,再从上到下计算矩阵dp
先计算第一行和第一列,再计算中间
- str1[i] == str2[j],dp[i][j] = dp[i-1][j-1]+1
- str1[i] != str2[j],dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
算出dp后,要根据dp找到公共子序列
1,从矩阵的右下角开始,有三种移动方式,向上、向左、 向左上。假设移动的过程中, i表
示此时的⾏数, j表示此时的列数,同时⽤⼀个变量res来表示最长公共子序列。
2,如果dp[i][j]⼤于dp[i-1][j]和dp[i][j-1],说明之前在计算dp[i][j]的时候,⼀定是选择了
决策dp[i-1][j-1]+1,可以确定str1[i]等于str2[j],并且这个字符⼀定属于最长公共子序列,
把这个字符放进res, 然后向左上方移动。
3,如果dp[i][j]等于dp[i-1][j],说明之前在计算dp[i][j]的时候, dp[i-1][j-1]+1这个决策不
是必须选择的决策,向上方移动即可。
4,如果dp[i][j]等于dp[i][j-1],与步骤3同理,向左方移动。
5,如果dp[i][j]同时等于dp[i-1][j]和dp[i][j-1],向上还是向下⽆所谓,选择其中⼀个即
可,反正不会错过必须选择的字符。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| public static int[][] getdp(char[] str1, char[] str2) { int[][] dp = new int[str1.length][str2.length]; //初始化第一行 for(int i = 0; i < str1.length; i++){ if(str1[i]==str2[0]){ dp[i][0] = 1; for(int j = i+1; j < str1.length; j++){ dp[j][0] = 1; } break; }else{ dp[i][0] = 0; } } //初始化第一列 for(int i = 1; i < str2.length; i++){ if(str1[0]==str2[i]){ dp[0][i] = 1; for(int j = i+1; j < str2.length; j++){ dp[0][j] = 1; } break; }else{ dp[0][i] = 0; } } for(int i = 1; i < str1.length; i++){ for(int j = 1;j < str2.length; j++){ dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]); if(str1[i] == str2[j]){ dp[i][j] = dp[i-1][j-1]+1; } } } return dp; } public static char[] getAns(String str1, String str2){ int m = str1.length()-1; int n = str2.length()-1; char[] char1 = str1.toCharArray(); char[] char2 = str2.toCharArray(); int[][] dp = getdp(char1, char2); int len = dp[m][n]; char[] str = new char[len]; while(len > 0){ //m>0 和n>0放在最前面,因为有可能过界导致后面 dp[m][n] == dp[m][n-1]的判断不能进行 if(n>0 && dp[m][n] == dp[m][n-1]){ n--; }else if(m>0 && dp[m][n] == dp[m-1][n]){ m--; }else{ //dp[m][n] == dp[m-1][n-1]+1应该是一个判断分支,但是不能写出该判断条件 //因为有可能到了第一行,m=0,无法计算m-1 str[--len] = char1[m]; m--; n--; } } return str; }
|