两个经典的动态规划问题。
最长公共子串问题
【题目】 求两个字符串str1和str2的最长公共子串的长度。
方法一 暴力法
思路:取短的字符串,求出其所有子串,然后在长的字符串中进行查找(String#contains方法)。
【代码】
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String s1 = sc.nextLine();
String s2 = sc.nextLine();
System.out.println(f(s1, s2));
}
}
private static int f(String s1, String s2) {
if (s1.length() < s2.length()) {
return f(s2, s1);
}
Set<String> set = new HashSet<>();
for (int i = 0; i < s2.length(); i++) {
for (int j = i + 1; j < s2.length() + 1; j++) {
set.add(s2.substring(i, j));
}
}
int max = 0;
for (String s : set) {
if (s.length() <= max) {
continue;
}
if (s1.contains(s)) {
max = Math.max(max, s.length());
}
}
return max;
}
}
方法二 动态规划
dp[i][j]的含义是:在必须把str1[i]和str2[j]当作公共子串的最后一个字符的情况下,公共子串的最大值。
那么:如果str1[i] == str2[j],dp[i][j] = dp[i - 1][j - 1] + 1;如果str1[i] != str2[j],dp[i][j] = 0。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String s1 = sc.nextLine();
String s2 = sc.nextLine();
System.out.println(f(s1, s2));
}
}
private static int f(String s1, String s2) {
int[][] dp = new int[s1.length()][s2.length()];
// 0 column
for (int i = 0; i < s1.length(); i++) {
dp[i][0] = s1.charAt(i) == s2.charAt(0) ? 1 : 0;
}
// 0 row
for (int i = 0; i < s2.length(); i++) {
dp[0][i] = s2.charAt(i) == s1.charAt(0) ? 1 : 0;
}
int max = 0;
for (int i = 1; i < s1.length(); i++) {
for (int j = 1; j < s2.length(); j++) {
if (s1.charAt(i) == s2.charAt(j)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
max = Math.max(max, dp[i][j]);
}
}
}
return max;
}
}
最长公共子序列问题
dp[i][j]的含义是:str[0..i]与str[0..j]的最长公共子序列的长度。
与最长公共子串有一点差别:
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
if (str1[i] == str2[j]) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1);
}