对于两个字符串,请设计一个时间复杂度为O(m*n)的算法(这里的m和n为两串的长度),求出两串的最长公共子串的长度。这里的最长公共子串的定义为两个序列U1,U2,..Un和V1,V2,...Vn,其中Ui + 1 == Ui+1,Vi + 1 == Vi+1,同时Ui == Vi。
给定两个字符串A和B,同时给定两串的长度n和m。
"1AB2345CD",9,"12345EF",7
返回:4
对于两个字符串,请设计一个时间复杂度为O(m*n)的算法(这里的m和n为两串的长度),求出两串的最长公共子串的长度。这里的最长公共子串的定义为两个序列U1,U2,..Un和V1,V2,...Vn,其中Ui + 1 == Ui+1,Vi + 1 == Vi+1,同时Ui == Vi。
给定两个字符串A和B,同时给定两串的长度n和m。
"1AB2345CD",9,"12345EF",7
返回:4
//经典动态规划问题,所以使用动态规划更方便一些吧 public int findLongest(String A, int n, String B, int m) { char[] arrA=A.toCharArray(); char[] arrB=B.toCharArray(); int[][] dp=new int[n][m]; int max=0; for(int i=0;i<n;i++){ if(arrA[i]==arrB[0]) dp[i][0]=1; } for(int j=1;j<m;j++){ if(arrB[j]==arrA[0]) dp[0][j]=1; } for(int i=1;i<n;i++){ for(int j=1;j<m;j++){ if(arrA[i]==arrB[j]) max=Math.max(dp[i][j]=dp[i-1][j-1]+1, max); } } return max; }
动态规划问题
class LongestSubstring: def findLongest(self, A, n, B, m): if n > m: A,B,n,m = B,A,m,n result = [[0 for i in range(m)] for j in range(n)] #result[i][j]表示子串A前i个和子串B前j个的最大公共子串 numMax = 0 for i in range(n): for j in range(m): if A[i]==B[j]: if i>0 and j>0: #第一个字符相等初始化为1,否则左上角加一 result[i][j] = result[i-1][j-1]+1 else: result[i][j] = 1 if numMax<result[i][j]: numMax = result[i][j] return numMax
结果对了,两个for循环时间复杂度好像不满足要求 import java.util.*; public class LongestSubstring { public int findLongest(String A, int n, String B, int m) { // write code here int max=0; for(int i=0;i<m;i++) { int temp=0; for(int j=i+1;j<m+1;j++) { String subStr=B.substring(i,j); if(A.contains(subStr)) { temp=j-i; } else{ break; } } if(max<temp) { max=temp; } } return max; } }
class LongestSubstring { public: int findLongest(string A, int n, string B, int m) { // write code here int i,j; if (0 == m || 0 == n) { return 0; } /*************getdp[][]******************/ int dp[m][n]; for (i = 0; i < m; i++) { if (B[i] == A[0]) { dp[i][0] = 1; } else dp[i][0] = 0; } for (j = 1; j < n; j++) { if (B[0] == A[j]) { dp[0][j] = 1; } else dp[0][j] = 0; } for (i = 1; i < m; i++) { for (j = 1; j < n; j++) { if (B[i] == A[j]) { dp[i][j] = dp[i - 1][j - 1] + 1; } else dp[i][j] = 0; } } int max = 0; for (i = 0; i < m; i++) { for (j = 0; j < n; j++) { if (dp[i][j] > max) { max = dp[i][j]; } } } return max; } };
public class LongestSubstring { public int findLongest(String A, int n, String B, int m) { // write code here int[][] dp = new int[n+1][m+1]; int res = 0; 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]>0?dp[i][j]+1:1; res = Math.max(res,dp[i+1][j+1]); } } } return res; } }
DP解法:时间空间复杂度都为log(m*n) class LongestSubstring { public: int findLongest(string A, int n, string B, int m) { if (n == 0 || m == 0) return 0; return getdp(A,n,B,m); //如果返回公共最长字符串则: //int max=0;int end = 0; //for (int i = 0; i<n; i++) //{ // for (int j = 0; j<m; j++){ // if (dp[i][j]>max) // { // max = dp[i][j];end=i; // } // } //} //return A.sub(end-max+1,max); } int getdp(string arrA, int n, string arrB, int m) { int res=0; vector<vector<int> >dp(n, vector<int>(m, 0)); for (int i = 0; i<n; i++){ if (arrA[i] == arrB[0]) dp[i][0] = 1; } for (int j = 1; j<m; j++){ if (arrB[j] == arrA[0]) dp[0][j] = 1; } for (int i = 1; i<n; i++){ for (int j = 1; j<m; j++){ if (arrA[i] == arrB[j]) dp[i][j] = dp[i - 1][j - 1] + 1; res = max(dp[i][j], res); } } return res; } }; 暴力解法: class LongestSubstring { public: int findLongest(string A, int n, string B, int m) { // write code here if (n == 0 || m == 0) return 0; int res = 0, max = 0; for (int i = 0; i<n; i++) { for (int j = 0; j<m; j++){ int tmpi = i; int tmpj = j; while (tmpi<n&&tmpj<m&&A[tmpi] == B[tmpj]) { res++; tmpi++; tmpj++; } if (res>max) { max = res; } res = 0; } } return max; } };
import java.util.*; public class LongestSubstring { public int findLongest(String A, int n, String B, int m) { // write code here int[] record = new int[n]; int maxLength = 0; for (int i = 0; i < m; i++) { for (int j = n - 1; j >= 0; j--) { if (A.charAt(j) == B.charAt(i)) { if (j == 0) { record[j] = 1; } else { record[j] = record[j-1] + 1; } maxLength = Math.max(record[j], maxLength); } else { record[j] = 0; } } } return maxLength; } }
class LongestSubstring { public: int findLongest(string A, int n, string B, int m) { int dp[1000][1000]; string new_a="#"+A; string new_b="#"+B; for(int i=0;i<=n;i++) dp[0][i]=0; for(int i=0;i<=m;i++) dp[i][0]=0; int max=-99; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(new_a[i]==new_b[j]){ dp[i][j]=dp[i-1][j-1]+1; if(dp[i][j]>max) max=dp[i][j]; } else dp[i][j]=0; } } return max; } };
class LongestSubstring { public: int findLongest(string A, int n, string B, int m) { vector<vector<int> > dp(n+1,vector<int>(m+1,0)); int Max = 0; for(int i=0;i<=n;i++) if(A[i]==B[0]) dp[i][0] = 1; for(int j=1;j<=m;j++) if(B[j]==A[0]) dp[0][j]=1; for(int i=1;i<n;i++) for(int j=1;j<m;j++) if(A[i]==B[j]) { dp[i][j] = dp[i-1][j-1] + 1; Max = max(Max, dp[i][j]); } return Max; } };
package dp; /** 最长公共子串 题目描述: 对于两个字符串,请设计一个时间复杂度为O(m*n)的算法(这里的m和n为两串的长度), 求出两串的最长公共子串的长度。这里的最长公共子串的定义为两个序列U1,U2,..Un和V1,V2,...Vn, 其中Ui + 1 == Ui+1,Vi + 1 == Vi+1,同时Ui == Vi。 给定两个字符串A和B,同时给定两串的长度n和m。 测试样例: "1AB2345CD",9,"12345EF",7 返回:4 */ public class LCSubstring { public int findLongest(String str1, int m, String str2, int n) { char[] s1 = str1.toCharArray(); char[] s2 = str2.toCharArray(); int[][] dp = new int[m+1][n+1]; int maxLen = 0; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (s1[i-1] == s2[j-1]) { dp[i][j] = dp[i-1][j-1]+1; maxLen = Math.max(maxLen, dp[i][j]); } else { dp[i][j] = 0; } } } return maxLen; } }
package alex.suda.dp; import java.util.Scanner; public class test3 { 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(findLongest(A, n, B, m)); } } public static int findLongest(String A, int n, String B, int m) { // d[i][j]是以A[i]和 B[j]开头的相同子串 int[][] d = new int[n + 1][m + 1]; // 初始化 int maxLen = 0; //这种逆向的动态规划思想值得学习 for (int i = n - 1; i >= 0; i--) { for (int j = m - 1; j >= 0; j--) { if (A.charAt(i) == B.charAt(j)) { d[i][j] = d[i + 1][j + 1] + 1; maxLen = Math.max(maxLen, d[i][j]); } } } return maxLen; } }
import java.util.*; public class LongestSubstring { public int findLongest(String A, int n, String B, int m) { // write code here int p = 0,max1 = 0,count = 0,max = 0; for(int d = 0; d < n; d++){ for(int i = 0; i < m; i++){ p = (d+i)%n; if(p==0){ count = 0; } if(A.charAt(p)==B.charAt(i)){ count++; max1 = Math.max(count,max1); }else{ count = 0; } } max = Math.max(max1,max); count=0; } return max; } }
//非动态规划,时间复杂度o(m*n),空间复杂度o(1); class LongestSubstring { public: int findLongest(string A, int n, string B, int m) { int R=0,max; for(int i=0;i<n;i++){ max=0; for(int j=0;j<m;j++){ if(i+j<n&&A[i+j]==B[j]){ max++; if(max>R) R=max; }else{ max=0; } } } for(int i=0;i<m;i++){ max=0; for(int j=0;j<n;j++){ if(i+j<m&&B[i+j]==A[j]){ max++; if(max>R) R=max; }else{ max=0; } } } return R; } };
时间复杂度是o(n^2),方法比较笨,就是采用的依次遍历,从头到尾, 让两次字符串的字串去依次比较,最后得出结果 public class LongestSubstring { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub new LongestSubstring().findLongest("bcbab",5,"abbabccc",8); } public int findLongest(String A, int n, String B, int m) { // write code here int sum = 0; int temp = 0; int flag = 0; int beginJ = 0; for (int i = 0; i < n; i++) { int beginI = i; for (int j = 0; j < m; j++) { if (flag == 0) { beginJ = j; flag = 1; } if (i < n && j < m && A.charAt(i) == B.charAt(j)) { i++; temp++; } else if (i < n && j < m && A.charAt(i) != B.charAt(j)) { flag = 0; temp = 0; i = beginI; j = beginJ; } if (sum < temp) { sum = temp; } if (j == m - 1) { flag = 0; i = beginI; temp = 0; } } } System.out.println(sum); return sum; } }
public class LongestSubstring { public int findLongest(String A, int n, String B, int m) { if(n == 0 || m == 0){ return 0; } //初始化状态矩阵 int[][] matrix = new int[n][m]; for(int i = 0; i < n; i++){ for(int j = 0; j < m; j++){ matrix[i][j] = 0; } } 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){ matrix[i][j] = 1; }else{ matrix[i][j] = matrix[i-1][j-1] + 1; } max = (max > matrix[i][j] ? max : matrix[i][j]); } } } return max; } }
下面是字符串21232523311324和字符串312123223445的匹配矩阵,前者为X方向的,后者为Y方向的。不难找到,红色部分是最长的匹配子串。通过查找位置我们得到最长的匹配子串为:21232
0 0 0 1 0 0
0 1 1 0 0 1 0 0 0
0 1 0 0 0 0 0 0 0 1 1 0 0 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1
0 0
0 1 0 0 0 0 0
0 0 1 1 0 0 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0
0
0 0 0 1 0 0 0 1 1 0
0 1 0 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
1
0 1 0 1 0 1 0 0 0 0 0 1 0 0
0 0 0 1 0 0 0 1 1 0 0 1 0 0
0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0
0 1 0
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0
但是在0和1的矩阵中找最长的1对角线序列又要花去一定的时间。通过改进矩阵的生成方式和设置标记变量,可以省去这部分时间。
0 0 0 1 0 0 0 1
1 0 0 1 0 0 0
0 1 0 0 0 0 0 0 0 2 1 0 0 0 0
1 0 2 0 1 0 1 0 0 0 0 0 1
0 0
0 2 0 0 0 0 0
0 0 1 1 0 0 0 0
1 0 3 0 1 0 1 0 0 0 0 0 1 0
0
0 0 0 4 0 0 0 2 1 0
0 1 0 0 0
1 0 1 0 5 0 1 0 0 0 0 0 2 0 0
1
0 1 0 1 0 1 0 0 0 0 0 1 0 0
0 0 0 2 0 0 0 2 1 0 0 1 0 0
0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0
0 1 0
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0
当字符匹配的时候,我们并不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断当前生成的元素的值是不是最大的,据此来改变标记变量的值,那么到矩阵完成的时候,最长匹配子串的位置和长度就已经出来了。
public class LongestSubstring { public int findLongest(String A, int n, String B, int m) { char[] Ac = A.toCharArray(); char[] Bc = B.toCharArray(); int res = 0; //既然每一个dp[i][j]的值的确定只是有关于斜上方的,那么就可以将dp的空间复杂度完全降低到o(1) // 遍历dp[][] 的另外一种方式,斜着打印 for(int i = Bc.length-1; i >= 0; --i){ // first ,init the first prepare value int curV = Ac[0] == Bc[i] ? 1 : 0; for(int j = i+1,k = 1; (k < Ac.length && j < Bc.length); ++j, ++k){ curV = Ac[k] == Bc[j] ? curV+1 : 0; res = Math.max(res, curV); } } for(int i = 1; i < Ac.length; ++i){ int curV = Bc[0] == Ac[i] ? 1 : 0; for(int j = i+1, k = 1; (k < Bc.length && j < Ac.length); ++j, ++k){ curV = Ac[j] == Bc[k] ? curV+1 : 0; res = Math.max(res, curV); } } return res; } }其实最开始的思路肯定还是dp[][]数组的判断的,也就是一定要注意规定好dp[i][j]的定义,也就是他被更新的来源,表示的是以i和j位置上的字符为结尾的公共字串的最大长度,只有这样才能利用dp的中的前面求得值进行更新,也就是dp[i-1][j-1]的值+1,如果不是这个以i和j位置上的字符为结尾的公共字串的最大长度条件的话,更新的值就会有错,然后优化的话就是,你再更新的时候发现这个dp[i][j]的值只和一个值相关,那就完全可以用一个变量搞定,转而变成了一个矩阵的斜着遍历的方式