最短公共超序列
思路
可以化作相同类型的子问题,可使用动态规划,但实现起来会比较麻烦,其较为复杂,dp数组中不是很明确的能够确定一种类型。这题较为麻烦且复杂,我从最开始的递归开始学习。
代码实现
暴力递归
超出时间限制
class Solution {
public String shortestCommonSupersequence(String str1, String str2) {
if(str1.isEmpty()) return str2;
if(str2.isEmpty()) return str1;
var s1 = str1.substring(0, str1.length()-1);
var s2 = str2.substring(0, str2.length()-1);
var x = str1.charAt(str1.length()-1);
var y = str2.charAt(str2.length()-1);
if(x == y)
return shortestCommonSupersequence(s1, s2) + x;
var ans1 = shortestCommonSupersequence(s1, str2);
var ans2 = shortestCommonSupersequence(str1, s2);
if(ans1.length() < ans2.length())
return ans1 + x;
return ans2 + y;
}
}
记忆化搜索
超出空间限制优化了时间,但是超出空间限制
class Solution {
private String s, t;
private String[][] dir;
public String shortestCommonSupersequence(String str1, String str2) {
s = str1;
t = str2;
dir = new String[s.length()][t.length()];
return dfs(s.length() - 1, t.length() - 1);
}
private String dfs(int i, int j){
if(i < 0) return t.substring(0, j + 1);
if(j < 0) return s.substring(0, i + 1);
if(dir[i][j] != null) return dir[i][j];
if(s.charAt(i) == t.charAt(j))
return dir[i][j] = dfs(i - 1, j - 1) + s.charAt(i);
var ans1 = dfs(i - 1, j);
var ans2 = dfs(i, j - 1);
if(ans1.length() < ans2.length())
return dir[i][j] = ans1 + s.charAt(i);
return dir[i][j] = ans2 + t.charAt(j);
}
}
优化后的记忆化
因为使用string数组会超出空间限制,所以记忆化数组中,就不能够存放string类型,不过只存放最长字序列长度也是可以的。
class Solution {
private String s, t;
private int[][] dir;
public String shortestCommonSupersequence(String str1, String str2) {
s = str1;
t = str2;
dir = new int[s.length()][t.length()];
dfs(s.length() - 1, t.length() - 1);
return answer(s.length() - 1, t.length() - 1);
}
private int dfs(int i, int j){
if(i < 0) return j + 1;
if(j < 0) return i + 1;
if(dir[i][j] > 0) return dir[i][j];
if(s.charAt(i) == t.charAt(j))
return dir[i][j] = dfs(i - 1, j - 1) + 1;
return dir[i][j] = Math.min(dfs(i-1, j), dfs(i, j-1)) + 1;
}
private String answer(int i, int j){
if(i < 0) return t.substring(0, j + 1);
if(j < 0) return s.substring(0, i + 1);
if(s.charAt(i) == t.charAt(j))
return answer(i - 1, j - 1) + s.charAt(i);
if(dfs(i, j - 1) < dfs(i - 1, j))
return answer(i, j - 1) + t.charAt(j);
return answer(i - 1, j) + s.charAt(i);
}
}
动态规划(递推)
class Solution {
public String shortestCommonSupersequence(String str1, String str2) {
char[] s = str1.toCharArray();
char[] t = str2.toCharArray();
int n = s.length, m = t.length;
var f = new int[n + 1][m + 1];
// f[i][j]表示字符串 s 以 s[i] 为结尾,t 以 t[j] 结尾的最短公共超序列长度
for(int i = 1; i < m; i++) f[0][i] = i;
for(int i = 1; i < n; i++) f[i][0] = i;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
if(s[i] == t[j])
f[i + 1][j + 1] = f[i][j] + 1;
else
f[i+1][j+1] = Math.min(f[i+1][j], f[i][j+1]) + 1;
int mx = f[n][m];
var ans = new char[mx];
for(int i = n-1, j = m-1, k = mx-1; ; ){
if(i < 0){
System.arraycopy(t, 0, ans, 0, j + 1);
break;
}
if(j < 0){
System.arraycopy(s, 0, ans, 0, i + 1);
break;
}
if(s[i] == t[j]){
ans[k--] += s[i--];
j--;
}else if(f[i + 1][j + 1] == f[i][j + 1] + 1){
ans[k--] += s[i--];
}else{
ans[k--] += t[j--];
}
}
return new String(ans);
}
}