最全的最长公共子序列&最长公共子串的不同题型

转:https://blog.csdn.net/qq_19446965/article/details/81668047

1.最长公共子序列的长度

题目:对于两个字符串,请设计一个高效算法,求他们的最长公共子序列的长度,这里的最长公共子序列定义为有两个序列U1,U2,U3...Un和V1,V2,V3...Vn,其中Ui&ltUi+1,Vi&ltVi+1。且A[Ui] == B[Vi]。

给定两个字符串AB,同时给定两个串的长度nm,请返回最长公共子序列的长度。保证两串长度均小于等于300。

样例:

"1A2C3D4B56",10,"B1D23CA45B6A",12
返回:

6

解析:求不连续的公共子序列的长度,O(n2)

dp[ i ][ j ] 为A的0 - i 的子串与B的0 - i 的子串的最大公共子序列长度

// 最长公共子序列,动规,O(n2)

public static int findLCS(String A, String B) {

int n = A.length();

int m = B.length();

int[][] dp = new int[n + 1][m + 1];

for (int i = 0; i < n; ++i) {

for (int j = 0; j < m; ++j) {

if (A.charAt(i) == B.charAt(j)) {

dp[i + 1][j + 1] = dp[i][j] + 1;

} else {

dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]);

}

}

}

return dp[n][m];

}

2.打印最长公共子序列

同上,回溯打印。​​​​​​​

// 打印最长公共子序列,动规,O(n2)

public static String findLCS2(String A, String B) {

int n = A.length();

int m = B.length();

int[] path = new int[n];

StringBuffer ret = new StringBuffer();

int[][] dp = new int[n + 1][m + 1];

for (int i = 0; i < n; ++i) {

for (int j = 0; j < m; ++j) {

if (A.charAt(i) == B.charAt(j)) {

dp[i + 1][j + 1] = dp[i][j] + 1;

} else {

dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]);

}

}

}

int i = n;

int j = m;

int k = 0;

while (dp[i][j] > 0) {

if (dp[i][j] == dp[i - 1][j])

i--;

else if (dp[i][j] == dp[i][j - 1])

j--;

else {

path[k++] = i - 1;

i--;

j--;

}

}

for (i = k - 1; i >= 0; i--)

ret.append(A.charAt(path[i]));

return ret.toString();

}

3.最长公共子串的长度

题目:对于两个字符串,请设计一个时间复杂度为O(m*n)的算法(这里的m和n为两串的长度),求出两串的最长公共子串的长度。这里的最长公共子串的定义为两个序列U1,U2,..Un和V1,V2,...Vn,其中Ui + 1 == Ui+1,Vi + 1 == Vi+1,同时Ui == Vi。

给定两个字符串AB,同时给定两串的长度nm

样例:

扫描二维码关注公众号,回复: 2772247 查看本文章
"1AB2345CD",9,"12345EF",7

返回:4

解析:求连续的公共子串的长度,O(n2)

dp[ i ][ j ] 为A的以 i 结尾的和子串与B的以 j 结尾的子串最大公共子串长度


// 最长公共子串,动规,O(n2)
	public int findLongest(String A, String B) {
		int n = A.length();
		int m = B.length();
		int[][] dp = new int[n][m];
		int max = 0;
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				if (A.charAt(i) == B.charAt(j)) {
					if (i == 0 || j == 0) {
						dp[i][j] = 1; // if(arrA[i]==arrB[0])dp[i][0]=1;if(arrB[j]==arrA[0])dp[0][j]=1;
					} else {
						dp[i][j] = dp[i - 1][j - 1] + 1;
					}
					max = Math.max(dp[i][j], max);
				}
			}
		}
		return max;
	}

4.打印最长公共子串 

例子同上,稍作修改便可:记录最大连续结尾位置

 
/ 打印最长公共子串,动规,O(n2)
	public static String findLongest2(String A, String B) {
		int n = A.length();
		int m = B.length();
		int index_i = 0;// 记录最长公共子串结尾位置
		int[][] dp = new int[n][m];
		int max = 0;
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				if (A.charAt(i) == B.charAt(j)) {
					if (i == 0 || j == 0) {
						dp[i][j] = 1; // if(arrA[i]==arrB[0])dp[i][0]=1;if(arrB[j]==arrA[0])dp[0][j]=1;
					} else {
						dp[i][j] = dp[i - 1][j - 1] + 1;
					}
					if (max < dp[i][j]) {
						max = dp[i][j];
						index_i = i;
					}
 
				}
			}
		}
		String result = A.substring(index_i - max + 1, index_i + 1);
		return result;
	}
5.A串变为

5.A串变为B串所需要的最少代价

题目:对于两个字符串A和B,我们需要进行插入、删除和修改操作将A串变为B串,定义c0,c1,c2分别为三种操作的代价,请设计一个高效算法,求出将A串变为B串所需要的最少代价。
给定两个字符串A和B,及它们的长度和三种操作代价,请返回将A串变为B串所需要的最小代价。保证两串长度均小于等于300,且三种代价值均小于等于100。
样例:
        "abc",3,"adc",3,5,3,100
返回:8

解析:

dp[i][j]表示A[0..i-1]变到B[0..j-1]需要的最小代价

长度为i的A修改为长度为j的B可以分为:
1、长度为i的A修改为长度为j-1的B,然后插入j位置的字符;
2、长度为i-1的A修改为长度为j的B,然后删除i位置的字符;
3、长度为i-1的A修改为长度为j-1的B,然后i位置的字符修改为j位置的字符。

 

public static int Str1ToStr2(String A, String B, int c0, int c1, int c2) {
		int n = A.length();
		int m = B.length();
		int[][] dp = new int[n + 1][m + 1];// dp[i][j]表示A[0..i-1]变到B[0..j-1]需要的最小代价
		for (int i = 1; i < n + 1; i++) {
			dp[i][0] = dp[i - 1][0] + c1;// 删除
		}
		for (int j = 1; j < m + 1; j++) {
			dp[0][j] = dp[0][j - 1] + c0;// 插入
		}
		for (int i = 1; i < n + 1; i++) {
			for (int j = 1; j < m + 1; j++) {
				if (A.charAt(i - 1) == B.charAt(j - 1)) {
					dp[i][j] = dp[i - 1][j - 1];
				} else {
					int cost1 = dp[i][j - 1] + c0;// 插入时的代价
					int cost2 = dp[i - 1][j] + c1;// 删除的代价
					int cost3 = dp[i - 1][j - 1] + c2;// 修改的代价
					dp[i][j] = Math.min(cost3, Math.min(cost1, cost2));
				}
			}
		}
		return dp[n][m];
	}
6.由两个字

6.由两个字符串交错组成

题目:对于三个字符串A,B,C。我们称C由A和B交错组成当且仅当C包含且仅包含A,B中所有字符,且对应的顺序不改变。请编写一个高效算法,判断C串是否由A和B交错组成。

给定三个字符串A,BC,及他们的长度。请返回一个bool值,代表C是否由A和B交错组成。保证三个串的长度均小于等于100。

样例:

    "ABC",3,"12C",3,"A12BCC",6
返回:true

解析:判断C串是否由A和B交错组成。

dp[ i ][ j ] : A的前个字符与B的前j个字符是否与C的第前i+j-1个字符匹配


public static boolean chkMixture(String A, String B, String C) {
		char[] a = A.toCharArray();
		char[] b = B.toCharArray();
		char[] c = C.toCharArray();
		int n = a.length;
		int m = b.length;
		int v = c.length;
		if (m + n != v) {
			return false;
		}
		boolean[][] dp = new boolean[n + 1][m + 1];// A的前个字符与B的前j个字符是否与C的第前i+j-1个字符匹配
		dp[0][0] = true;
		for (int i = 1; i <= n; i++) {
			if (a[i - 1] == c[i - 1]) {// 只有A的字符串与C匹配
				dp[i][0] = true;
			} else {
				break;
			}
		}
 
		for (int j = 1; j <= m; j++) {
			if (b[j - 1] == c[j - 1]) {// 只有B的字符串与C匹配
				dp[0][j] = true;
			} else {
				break;
			}
		}
 
		for (int i = 1; i < n + 1; i++) {
			for (int j = 1; j < m + 1; j++) {
				if (dp[i - 1][j] && a[i - 1] == c[i + j - 1]) {
					dp[i][j] = true;
					continue;
				}
				if (dp[i][j - 1] && b[j - 1] == c[i + j - 1]) {
					dp[i][j] = true;
				}
			}
		}
		return dp[n][m];
	}
 

猜你喜欢

转载自blog.csdn.net/qq_40086556/article/details/81671558