对于两个字符串,请设计一个高效算法,求他们的最长公共子序列的长度,这里的最长公共子序列定义为有两个序列U1,U2,U3...Un和V1,V2,V3...Vn,其中Ui<Ui+1,Vi<Vi+1。且A[Ui] == B[Vi]。
给定两个字符串A和B,同时给定两个串的长度n和m,请返回最长公共子序列的长度。保证两串长度均小于等于300。
测试样例:
"1A2C3D4B56",10,"B1D23CA45B6A",12
返回:6
对于两个字符串,请设计一个高效算法,求他们的最长公共子序列的长度,这里的最长公共子序列定义为有两个序列U1,U2,U3...Un和V1,V2,V3...Vn,其中Ui<Ui+1,Vi<Vi+1。且A[Ui] == B[Vi]。
给定两个字符串A和B,同时给定两个串的长度n和m,请返回最长公共子序列的长度。保证两串长度均小于等于300。
"1A2C3D4B56",10,"B1D23CA45B6A",12
返回:6
import java.util.*; public class LCS { public int findLCS(String A, int n, String B, int m) { int len1=n,len2=m; int[][] res=new int[len1+1][len2+1]; for(int i=0;i<len1;i++){ for(int j=0;j<len2;j++){ int cur=0; if(A.charAt(i)==B.charAt(j)) cur++; res[i+1][j+1]=maxNum(res[i][j]+cur,res[i][j+1],res[i+1][j]); } } return res[len1][len2]; } /* * 返回三者最大值 */ private int maxNum(int i, int j, int k) { int max = i; max = j > max ? j : max; max = k > max ? k : max; return max; } }
import java.util.*; public class LCS { public int findLCS(String A, int n, String B, int m) { // write code here char[] a = A.toCharArray(); char[] b = B.toCharArray(); int[][] dp = new int[n + 1][m + 1]; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { if (a[i - 1] == b[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } return dp[n][m]; } }
//第一次做这个题,不过看思路懂了。自己也做出来了 /* 0 1 2 3 4 5 j i0 1 2 3 4 i=j=0时,dp[i][j]=0 A[i]=B[j]时(从1计数),dp[i][j]=dp[i-1][j-1]+1 A[i]!=B[j]时,dp[i][j]=max(dp[i][j-1],dp[i-1][j]) */ import java.util.*; public class LCS { public int findLCS(String A, int n, String B, int m) { // 行对应A,比A长1,原因是0作为初始化。列同理。dp[n][m]指A[n-1],B[m-1] int[][] dp = new int[n+1][m+1]; for(int i=0;i<dp.length;i++){ dp[i][0]=0; } for(int i=0;i<dp[0].length;i++){ dp[0][i]=0; } //事实上,数组默认也是0 for(int i=1;i<dp.length;i++){ for(int j=1;j<dp[0].length;j++){ //如果是字符串,用equals,由于charAt()是字符,可以运算,故可以用== if(A.charAt(i-1)==B.charAt(j-1)){//A[0]==B[0],对应dp[1][1] dp[i][j]=dp[i-1][j-1]+1; }else{ dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]); } } } return dp[n][m]; } }
import java.util.*; public class LCS { public int findLCS(String A, int n, String B, int m) { return getIncrementSequence(A,B); } public int getIncrementSequence(String A, String B) { int a_len = A.length(); int b_len = B.length(); int[][] dp = new int[a_len+1][b_len+1]; int size = 0; for (int i = 1; i <= a_len; i++) { for (int j = 1; j <= b_len; j++) { if (A.charAt(i-1) == B.charAt(j-1)) { dp[i][j] = dp[i - 1][j - 1] + 1; if (dp[i][j] > size) { size = dp[i][j]; } } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } return size; } }
public class LCS { public int findLCS(String A, int n, String B, int m) { // write code here char[] a = A.toCharArray(),b = B.toCharArray(); int[][] dp = new int[n+1][m+1]; for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ dp[i+1][j+1]= a[i]==b[j]?dp[i][j]+1 : Math.max(dp[i][j+1],dp[i+1][j]); } } return dp[n][m]; } }
def findLCS(self, A, n, B, m): #result[i][j]保存A前i个子串和B前j个子串的公共子序列 result = [[0 for i in range(m+1)] for j in range(n+1)] for i in range(1,n+1): for j in range(1,m+1): result[i][j] = max(result[i-1][j],result[i][j-1]) #默认传承之前的公共子序列长度 if A[i-1]==B[j-1]: result[i][j] = result[i-1][j-1]+1 #等于子串都减一的公共子序列长度加一 return result[-1][-1]
思路(参考程序员代码面是指南),DP表,dp[i]][j]代表A[0...i]与B[0...j]的最长公共子序列; 我们先求出第一行与第一列的DP值,然后在求里面的值,里面的值有三种情况:1.来自dp[i-1][j]; 2.来自dp[i][j-1]; 3.A[i]==B[j]时为dp[i-1][j-1];我们求出这三者的最大值即可,用一个res来计算。最后返回即可。
class LCS
{ public: int findLCS(string A, int n, string B, int m) { // write code here if (n == 0 || m == 0) return 0; return getdp(A,n,B,m); } private: int getdp(string arrA, int n, string arrB, int m) { int res=0; vector<vector<int> >dp(n, vector<int>(m, 0)); dp[0][0]=(arrA[0]==arrB[0]?1:0); for (int i = 1; i<n; i++){ dp[i][0]=max(dp[i-1][0],arrA[i]==arrB[0]?1:0); } for (int j = 1; j<m; j++){ dp[0][j]=max(dp[0][j-1],arrA[0]==arrB[j]?1:0); } for (int i = 1; i<n; i++){ for (int j = 1; j<m; j++){ dp[i][j]=max( dp[i - 1][j] , dp[i][j-1]) ; res=max( res ,dp[i][j]); if (arrA[i] == arrB[j]) { dp[i][j]=max(dp[i][j] , dp[i - 1][j - 1] + 1); res = max( res, dp[i][j]); } } } return res; } };
import java.util.*; public class LCS { public static int findLCS(String A, int n, String B, int m) { // write code here 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]; } }
import java.util.*; public class LCS { public int findLCS(String A, int n, String B, int m) { int[][] dp = new int[n + 1][m + 1]; 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] + 1; else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } return dp[n][m]; } }
package alex.suda.dp; import java.util.Scanner; public class test2 { public static void main(String[] args) { // TODO Auto-generated method stub Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { int n = scanner.nextInt(); String A = scanner.next(); int m = scanner.nextInt(); String B = scanner.next(); System.out.print(findLCS(A, n, B, m)); } } public static int findLCS(String A, int n, String B, int m) { // 设d[i][j]为字符串A的1~i位和B的1~j位的最大公共子序列的长度 // 则d[i][j] = d[i-1][j-1] + 1, 如果A[i] = A[j] // 否则 d[i][j] = max{d[i-1][j],d[i][j-1]} int[][] d = new int[n + 1][m + 1]; //初始化 for (int i = 0; i <= n; i++) { d[i][0] = 0; } for (int j = 0; j <= m; j++) { d[0][j] = 0; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (A.charAt(i-1) == B.charAt(j-1)) { d[i][j] = d[i - 1][j - 1] + 1; } else { d[i][j] = Math.max(d[i - 1][j], d[i][j - 1]); } } } return d[n][m]; } }
class LCS { public: int findLCS(string A, int n, string B, int m) { // write code here int dp[305][305]; memset(dp,0,sizeof(dp)); for(int i = 0;i<n;i++) for(int j = 0;j<m;j++){ if(A[i] == B[j]) dp[i+1][j+1] = dp[i][j] + 1; else dp[i+1][j+1] = max(dp[i][j+1],dp[i+1][j]); } return dp[n][m]; } };
int findLCS(string A, int n, string B, int m) { int len = 0; int **temp = new int*[n+1]; for(int i=0; i<n+1; i++){ temp[i] = new int[m+1]; temp[i][0] = 0; } for(int j=0; j<m+1; j++){ temp[0][j] = 0; } for(int i=1; i<n+1; i++){ for(int j=1; j<m+1; j++){ if(A.at(i-1) == B.at(j-1)){ temp[i][j] = temp[i-1][j-1] + 1; len = len<temp[i][j]?temp[i][j]:len; }else{ temp[i][j] = temp[i][j-1]<temp[i-1][j]?temp[i-1][j]:temp[i][j-1]; } } } return len; }
import java.util.*; public class LCS { public int findLCS(String A, int n, String B, int m) { // 先确定好长度大小的 String small = A.length() <= B.length() ? A : B; String big = A.length() > B.length() ? A : B; // 动态滚动数组 -- 因为涉及到的dp在求得每一个dp[i][j]的时候是不分 int[] dp = new int[Math.min(n, m)]; // 初始化动态滚动数组 for(int i = 0; i < dp.length; ++i){ dp[i] = (big.charAt(0) == small.charAt(i) || (i > 1 && dp[i-1] == 1)) ? 1 : 0; } // 滚动更新动态数组 // need to record the preVal int preValRecord = 0; // the golbal val for this below int preValForRefreshDp = 0; // also the global value for this below for(int i = 1; i < big.length(); ++i){ for( int j = 0; j < dp.length; ++j){ // first to refresh the first column for the dp[][] -- mean to the dp[0] // need to record the preVal preValRecord = dp[j]; if(j == 0){ dp[0] = (small.charAt(0) == big.charAt(i) || dp[0] == 1) ? 1 : 0; }else{ dp[j] =(big.charAt(i) != small.charAt(j)) ? Math.max(dp[j], dp[j-1]) : preValForRefreshDp+1; } preValForRefreshDp = preValRecord; } } return dp[dp.length-1]; } }看到上面都是用的dp,并且二维的,那我就贴一个一维的滚动数组的优化吧,还是得益于左神的思想,当然要想时间再缩短的话,就像字符串的处理一下,变成char数组,毕竟数组的查找会更快嘛。这里我主要是展示思想,就直接用的string对象的charAt的方式
class LCS { public: int findLCS(string A, int n, string B, int m) { int dp[n+1][m+1]; for(int i=0;i<=n;i++) dp[i][0] = 0; for(int j=0;j<=m;j++) dp[0][j] = 0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) { if(A[i] == B[j]) dp[i+1][j+1] = dp[i][j] + 1; else dp[i+1][j+1] = max(dp[i+1][j],dp[i][j+1]); } return dp[n][m]; } };
import java.util.*; public class LCS { public int findLCS(String A, int n, String B, int m) { // write code here int[][] dp = new int[n + 1][m + 1]; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (A.charAt(i - 1) == B.charAt(j - 1)) { dp[i][j] = Math.max(dp[i - 1][j - 1] + 1, dp[i][j]); } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } return dp[n][m]; } }
import java.util.*; public class LCS { public int findLCS(String A, int n, String B, int m) { // write code here //dp[i][j]表示A里从0...i与B里从0...j的最长公共子序列长度 int[][] dp = new int[n][m]; //设置base case for (int i = 0; i < m; i++) { if (i > 0 && dp[0][i-1] == 1) { dp[0][i] = 1; continue; } if (B.charAt(i) == A.charAt(0)) dp[0][i] = 1; } for (int i = 0; i < n; i++) { if (i > 0 && dp[i-1][0] == 1) { dp[i][0] = 1; continue; } if (A.charAt(i) == B.charAt(0)) dp[i][0] = 1; } //若A的i位置 != B的j位置,则有取三种情况中最大值 //否则=A的0...i-1位置 与 B的0...j-1位置上最长公共子序列 + 1. for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { dp[i][j] = Math.max(dp[i-1][j-1], Math.max(dp[i-1][j], dp[i][j-1])); if (A.charAt(i) == B.charAt(j)) dp[i][j] = dp[i-1][j-1]+1; } } return dp[n-1][m-1]; } }
class LCS { public: int findLCS(string A, int n, string B, int m) { int c[n+1][m+1]; int i,j; for(i=0;i<=n;i++) c[i][0]=0; for(j=1;j<=m;j++) c[0][j]=0; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ if(A[i-1]==B[j-1]) c[i][j] = c[i-1][j-1] + 1; else if(c[i-1][j]>=c[i][j-1]) c[i][j] = c[i-1][j]; else c[i][j] = c[i][j-1]; } } return c[n][m]; } };
import java.util.*; public class LCS { public int findLCS(String A, int n, String B, int m) { int dp[][]=new int[n+1][m+1]; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(A.charAt(i-1)==B.charAt(j-1)){ dp[i][j]=dp[i-1][j-1]+1; }else{ dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]); } } } return dp[n][m]; } }
动态规划的经典问题,这里不多说。只mark一个小tips:dp维度设置为[n+1][m+1]避免了dp设置为[n][m]时判断i,j是否都小于1还是某一个小于1,这样就可以直接通过初始化dp三个特殊值:
dp[0][0] = 0;
dp[1][0] = 0;
dp[0][1] = 0;
后面可以直接将dp分两种情况处理即可。
直接上代码:
import java.util.*;
public class LCS {
public int findLCS(String A, int n, String B, int m) {
// write code here
int[][] dp = new int[n + 1][m + 1];
dp[0][0] = 0;
dp[1][0] = 0;
dp[0][1] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j ++) {
if (A.charAt(i-1) == B.charAt(j-1)) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[n][m];
}
}
class LCS {
public:
int findLCS(string A, int n, string B, int m) {
// write code here
int table[n + 1][m + 1];
memset(table, 0, sizeof(table));
for(int i = 1;i <= n;++i){
for(int j = 1;j <= m;++j){
if(A[i-1] == B[j-1])
table[i][j] = table[i-1][j-1] + 1;
else {
table[i][j] = max(table[i-1][j],table[i][j-1]);
}
}
}
return table[n][m];
}
};