动态规划:查找最长公共子序列

查找最长公共子序列

什么是最长公共子序列?

一个序列,如果是两个或多个已知序列的子序列,且是所有子序列中最长的,则为最长公共子序列。

例:
如下有两个字符串:
x:A B C B D A B
y:B D C A B A
查找它们最长公共子序列。
则它们公共子序列有(B D A B)(B D A B) (B C B A)

如何进行查找\思路?

动态规划:查找最长公共子序列_第1张图片
找最长公共子序列长度lcs(x,y)
lcs(x[0],x[0])=0
lcs(x[1],x[1])=0 如果不相等为0
lcs(x[2],x[2])=0分情况:
lcs(x[1],x[2])=0
lcs(x[2],x[1])=1
此时lcs(x[2],x[2])=1 即取两种情况的最大值
lcs(x[3],x[3])=2
lcs(x[4],x[4])=0分情况:
lcs(x[3],x[4])=2
lcs(x[4],x[3])=2
此时lcs(x[4],x[4])=2
lcs(x[5],x[5])=0分情况:
lcs(x[4],x[5)=3
lcs(x[5],x[4])=2
此时lcs(x[4],x[4])=3
。。。。。剩下的以此类推

伪代码如下:
lcs(x,y,i,j){
if(i0 or j0)return 0;
else if(x[i]==x[j])return lcs(x,y,i,j)+1;
else return max(lcs(x,y,i,j–),lcs(x,y,i-1,j));
}

key1:穷举法

穷举法需要遍历出所有的可能,在这不考虑。

key2: 递归方法

 static int f(char[] a, char[] b, int i, int j) {
	 if (i == 0 || j == 0) {
	 return 0;
	 } else if (a[i] == b[j]) {
	 return f(a, b, i - 1, j - 1) + 1;
	 } else {
	 return Max(f(a, b, i, j - 1), f(a, b, i - 1, j));
	 }
	 }
	
	 static int Max(int a, int b) {
	 if (a > b)
	 return a;
	 else
	 return b;
	 }
	
	 public static void main(String[] args) {
	
	 String s1 = " abcdfadskb";
	 String s2 = " abefaslf";
	 char[] c1 = new char[s1.length() + 1];// 带0号字符的新数组
	 char[] c2 = new char[s1.length() + 1];// 带0号字符的新数组
	 char[] t1 = s1.toCharArray();
	 char[] t2 = s1.toCharArray();
	 c1[0] = (char) 0;
	 c2[0] = (char) 0;
	 for (int i = 0; i < t1.length; i++) {
	 c1[i + 1] = t1[i];
	 }
	 for (int i = 0; i < t2.length; i++) {
	 c2[i + 1] = t2[i];
	 }
	 System.out.println(f(s1.toCharArray(), s2.toCharArray(), 4, 4));
	
	 }

key3:备忘录法

static int f(char[] a, char[] b, int i, int j, int[][] bak) {
		if (bak[i][j] != -1)
			return bak[i][j];
		if (i == 0 || j == 0) {
			bak[i][j] = 0;
		} else if (a[i] == b[j]) {
			bak[i][j] = f(a, b, i - 1, j - 1, bak) + 1;
		} else {
			bak[i][j] = Max(f(a, b, i, j - 1, bak), f(a, b, i - 1, j, bak));
		}
		return bak[i][j];
	}
	
	static int Max(int a, int b) {
		if (a > b)
			return a;
		else
			return b;
	}
	
	public static void main(String[] args) {
		
		String s1 = " ABCBDAB";
		String s2 = " BDCABA";
		char[] c1 = new char[s1.length() + 1];// 带0号字符的新数组
		char[] c2 = new char[s2.length() + 1];// 带0号字符的新数组
		char[] t1 = s1.toCharArray();
		char[] t2 = s1.toCharArray();
		c1[0] = (char) 0;//前面扩张一个位置0
		c2[0] = (char) 0;
		for (int i = 0; i < t1.length; i++) {
			c1[i + 1] = t1[i];
		}
		for (int i = 0; i < t2.length; i++) {
			c2[i + 1] = t2[i];
		}
		int[][] bak = new int[c1.length][c2.length];
		for (int i = 0; i < c1.length; i++) {
			for (int j = 0; j < bak[i].length; j++) {
				bak[i][j] = -1;
			}
		}
		System.out.println(f(c1, c2, c1.length - 1, c2.length - 1, bak));
		
	}

key4:自底向上

static int f(char[] a, char[] b, int i, int j, int[][] bak) {
		for(int ii=0;ii<=i;ii++) {
			for(int jj=0;jj<=j;jj++) {
				if (ii == 0 || jj == 0) {
					bak[ii][jj] = 0;
				} else if (a[ii] == b[jj]) {
					bak[ii][jj] = bak[ii-1][jj-1]+ 1;
				} else {
					bak[ii][jj] = Max(bak[ii-1][jj],bak[ii][jj-1]);
				}
			}
		}
		return bak[i][j];
	}

	static int Max(int a, int b) {
		if (a > b)
			return a;
		else
			return b;
	}

	public static void main(String[] args) {

		String s1 = "ABCBDAB";
		String s2 = "BDCABA";
//		char[] c1 = new char[s1.length() + 1];// 带0号字符的新数组
		char[] c1=new StringBuffer((char)0).append(s1).toString().toCharArray();
		char[] c2 = new char[s2.length() + 1];// 带0号字符的新数组
//		char[] t1 = s1.toCharArray();
		char[] t2 = s2.toCharArray();
//		c1[0] = (char) 0;
		c2[0] = (char) 0;
//		for (int i = 0; i < t1.length; i++) {
//			c1[i + 1] = t1[i];
//		}
		for (int i = 0; i < t2.length; i++) {
			c2[i + 1] = t2[i];
		}
		int[][] bak = new int[c1.length][c2.length];
		
		System.out.println(f(c1, c2, c1.length - 1, c2.length - 1, bak));

	}

你可能感兴趣的:(动态规划:查找最长公共子序列)