动态规划与备忘录的LCS实现
动态规划从下到上积累能量,中间值全部记录以方便后期计算时调用,但有些时候很多记录的值用不到,这个时候备忘录方法则更好,可以减少记录的值,其特点是自上到下,记录少部分值。以LCS最长公共子串问题威力,分别给出两种实现。
- 动态规划法:
package longestCommonSubstring;
public class LCS_1 {
public static void main(String[] args) {
// TODO 自动生成的方法存根
String s1 = "adfdsgdsgadsg";
String s2= "sddsgdsgafsdf";
LCS(s1, s2);
}
public static void LCS(String s1, String s2) {
char[] a = s1.toCharArray();
char[] b = s2.toCharArray();
int a_length = a.length;
int b_length = b.length;
int[][] lcs = new int[a_length + 1][b_length + 1];
for(int i = 0; i <= a_length; i++) {
lcs[i][0] = 0;
}
for(int j = 0; j <= b_length; j++) {
lcs[0][j] = 0;
}
for(int i = 1; i <= a_length; i++) {
for(int j = 1;j <= b_length; j++) {
if(a[i-1] == b[j-1]) {
lcs[i][j] = lcs[i-1][j-1] + 1;
}
else {
lcs[i][j] = Math.max(lcs[i-1][j], lcs[i][j-1]);
}
}
}
for (int i = 0; i <= a_length; i++) {
for (int j = 0; j <= b_length; j++) {
System.out.print(lcs[i][j]+",");
}
System.out.println();
}
System.out.println("s1和s2的最长公共子串长度是: " + lcs[a_length][b_length]);
LCS_Print(lcs,a,b);
}
private static void LCS_Print(int[][] lcs, char[] a, char[] b) {
int a_length = a.length;
int b_length = b.length;
int maxlen = lcs[a_length][b_length];
char[] comStr = new char[maxlen];
int i = a_length, j = b_length;
while(maxlen > 0) {
//起码有一个字母在公共子串里
if(lcs[i][j] != lcs[i-1][j-1]) {
if(lcs[i-1][j] == lcs[i][j-1]) {
comStr[maxlen-1] = a[i-1];
i--;j--;
maxlen--;
}
else if(lcs[i-1][j] > lcs[i][j-1]) {
i--;
}
else {
j--;
}
}
else {
i--;
j--;
}
}
System.out.print("最长公共子串是: ");
System.out.println(comStr);
}
}
- 备忘录方法:
package longestCommonSubstring;
public class LCS_2 {
static int[][] lcs;
public static void main(String[] args) {
// TODO 自动生成的方法存根
String s1 = "adfdsgdsgadsg";
String s2= "sddsgdsgafsdf";
LCS(s1, s2);
}
private static void LCS(String s1, String s2) {
// TODO 自动生成的方法存根
char[] a = s1.toCharArray();
char[] b = s2.toCharArray();
int a_length = a.length;
int b_length = b.length;
lcs = new int[a_length+1][b.length+1];
for(int i = 0; i <= a_length ;i++) {
for(int j = 0; j <= b_length; j++) {
lcs[i][j] = -1;
}
}
lookUpChain(a, b, a_length, b_length);
System.out.println("最长公共子串是:" + lookUpChain(a, b, a_length, b_length));
LCS_Print(a,b);
}
private static void LCS_Print(char[] a, char[] b) {
int a_length = a.length;
int b_length = b.length;
int maxlen = lcs[a_length][b_length];
char[] comStr = new char[maxlen];
int i = a_length, j = b_length;
while(maxlen > 0) {
//起码有一个字母在公共子串里
if(lcs[i][j] != lcs[i-1][j-1]) {
if(lcs[i-1][j] == lcs[i][j-1]) {
comStr[maxlen-1] = a[i-1];
i--;j--;
maxlen--;
}
else if(lcs[i-1][j] > lcs[i][j-1]) {
i--;
}
else {
j--;
}
}
else {
i--;
j--;
}
}
System.out.print("最长公共子串是: ");
System.out.println(comStr);
}
private static int lookUpChain(char[] a, char[] b,int i, int j) {
if(lcs[i][j]>-1)
return lcs[i][j];
if(i==0||j==0)
lcs[i][j]=0;
else{
if(a[i-1]==b[j-1])
lcs[i][j]=lookUpChain(a,b,i-1,j-1) + 1;
else
lcs[i][j]=Math.max(lookUpChain(a,b,i,j-1),lookUpChain(a,b,i-1,j));
}
return lcs[i][j];
}
}
在合适的问题上用合适的方法才是算法的智慧和精髓。