最长公共子序列解答:
public class Test1 {
public static void main(String[] args) {
char[] chars1 = "ABCBDAB".toCharArray();
char[] chars2 = "BDCABA".toCharArray();
LCS(chars1, chars2);
}
public static void LCS(char[] chars1, char[] chars2) { // 最长公共子序列
int len1 = chars1.length;
int len2 = chars2.length;
int[][] dp = new int[len1+1][len2+1];
dp[0][0] = 0;
for(int i = 1; i <= len1; i++) {
for(int j = 1; j <= len2; j++) {
if(chars1[i-1] == chars2[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]);
}
}
}
// System.out.println(dp[len1][len2]); // 最长公共子序列长度
getres(dp, len1, len2, chars1);
}
public static void getres(int[][] dp, int i, int j, char[] chars) {
StringBuilder sb = new StringBuilder();
while(i != 0 && j != 0) {
if(dp[i][j] == dp[i-1][j]) i--;
else if(dp[i][j] == dp[i][j-1]) j--;
else {
sb.append(chars[i-1]);
i--;
j--;
}
}
System.out.println(sb.reverse().toString());
}
}
其中最重要的就是整个算法思想中的递推式:
Leetcode 中用到类似思想的题目:
10. Regular Expression Matching
贴上解答:
class Solution {
public boolean isMatch(String s, String p) {
int len1 = s.length();
int len2 = p.length();
boolean[][] dp = new boolean[len1+1][len2+1];
dp[0][0] = true;
for(int j = 1; j <= len2; j++) {
if(p.charAt(j-1) == '*') {
dp[0][j] = dp[0][j-2];
}
}
for(int i = 1; i <= len1; i++) {
for(int j = 1; j <= len2; j++) {
if(p.charAt(j-1) == '*') {
if(p.charAt(j-2) == s.charAt(i-1) || p.charAt(j-2) == '.') {
dp[i][j] = dp[i][j-2] || dp[i][j-1] || dp[i-1][j];
} else {
dp[i][j] = dp[i][j-2];
}
}
else if(p.charAt(j-1) == '.' || p.charAt(j-1) == s.charAt(i-1)) dp[i][j] = dp[i-1][j-1];
else dp[i][j] = false;
}
}
return dp[len1][len2];
}
}
上面代码中的 dp[][] 二维数组,类比于 LCS 问题中的二维数组。
贴上一个较好的解析:
1, If p.charAt(j) == s.charAt(i) : dp[i][j] = dp[i-1][j-1];
2, If p.charAt(j) == '.' : dp[i][j] = dp[i-1][j-1];
3, If p.charAt(j) == '*':
here are two sub conditions:
1 if p.charAt(j-1) != s.charAt(i) : dp[i][j] = dp[i][j-2] //in this case, a* only counts as empty
2 if p.charAt(i-1) == s.charAt(i) or p.charAt(i-1) == '.':
dp[i][j] = dp[i-1][j] //in this case, a* counts as multiple a
or dp[i][j] = dp[i][j-1] // in this case, a* counts as single a
or dp[i][j] = dp[i][j-2] // in this case, a* counts as empty
贴上解答:
// 编辑距离,word1 和 word2,
// 计算将 word1 转化为 word2 的最小操作数
// 操作包括 插入一个字符,删除一个字符,替换一个字符
class Solution {
public int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
int[][] dp = new int[len1+1][len2+1];
for(int i = 0; i <= len1; i++) dp[i][0] = i;
for(int j = 0; j <= len2; j++) dp[0][j] = j;
// 其中一个长度为0,操作数等于另外一个的长度
int rp = 0, dl = 0, ins = 0;
for(int i = 1; i <= len1; i++) {
for(int j = 1; j <= len2; j++) {
if(word1.charAt(i-1) == word2.charAt(j-1)) {
dp[i][j] = dp[i-1][j-1];
} else {
dp[i][j] = Math.min(dp[i-1][j-1], Math.min(dp[i-1][j], dp[i][j-1]))+1;
}
}
}
return dp[len1][len2];
}
}
该题的递推式,个人觉得比较容易理解,是这几个题目中较简单的一个。
583. Delete Operation for Two Strings
贴上解答:
// 给定两个单词,找出使得 word1 和 word2 相同的最小步数
// 每步可以删除任意一个字符串中的一个字符
class Solution {
public int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
if(len1 == 0) return len2;
if(len2 == 0) return len1;
int[][] dp = new int[len1+1][len2+1];
for(int i = 0; i <= len1; i++) dp[i][0] = i;
for(int j = 0; j <= len2; j++) dp[0][j] = j;
// 其中一个为0, 步数等于另一个的长度.
for(int i = 1; i <= len1; i++) {
for(int j = 1; j <= len2; j++) {
if(word1.charAt(i-1) == word2.charAt(j-1)) {
dp[i][j] = dp[i-1][j-1];
} else {
dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + 1;
}
}
}
return dp[len1][len2];
}
}
多类比,多思考。嗯。