给定两个字符串str1和str2,输出两个字符串的最长公共子串
题目保证str1和str2的最长公共子串存在且唯一。
数据范围:
要求: 空间复杂度 ,时间复杂度
要求: 空间复杂度 ,时间复杂度
import java.util.*; public class Solution { /** * longest common substring * @param str1 string字符串 the string * @param str2 string字符串 the string * @return string字符串 */ public String LCS (String str1, String str2) { // write code here return process(str1,str2); } public static String process(String str1,String str2){ int p1 = str1.length()-1; int p2 = str2.length()-1; char[] cs1 = str1.toCharArray(); char[] cs2 = str2.toCharArray(); int[][] dp = new int[p2+1][p1+1]; int max = 0; int pmax = -1; for (int i = p2; i >= 0 ; i--){ for (int j = p1; j >= 0 ; j--){ if(cs1[j] == cs2[i]){ int count = 0; if (j+1 <= p1 && i+1 <= p2){ count = dp[i+1][j+1]; } dp[i][j] = count+1; if (dp[i][j] >= max ){ max = dp[i][j]; pmax = j; } } } } StringBuilder stringBuilder = new StringBuilder(""); for (int i = 0; i < max ; i++){ stringBuilder.append(cs1[pmax++]); } return stringBuilder.toString(); } }
JAVA实现,动态规划方法,优化一下空间复杂度。 import java.util.*; public class Solution { /** * longest common substring * @param str1 string字符串 the string * @param str2 string字符串 the string * @return string字符串 */ public String LCS (String str1, String str2) { // write code here if(str1 == null || str2 == null) return null; // char[] s1 = str1.toCharArray(); // char[] s2 = str2.toCharArray(); // int[][] dp = new int[str1.length()][str2.length()]; // int r = 0,c = 0; // //循环中判断特殊情况 // for(int i=0;i<s1.length;i++) { // for(int j=0;j<s2.length;j++) { // if(s1[i]==s2[j]) { // dp[i][j] = (i==0||j==0) ? 1 : dp[i-1][j-1]+1; // }else { // dp[i][j] = 0; // } // if(dp[i][j] > dp[r][c]) { // r=i; // c=j; // } // } // } //先将特殊情况在循环外赋值,少一次判断时间; // for(int i = 0;i<s1.length;i++) { // dp[i][0] = s1[i]==s2[0] ? 1 : 0; // } // for(int j = 0;j<s2.length;j++) { // dp[0][j]= s1[0]==s2[j] ? 1 : 0; // } // for(int i=1;i<s1.length;i++) { // for(int j=1;j<s2.length;j++) { // dp[i][j] = s1[i]==s2[j] ? dp[i-1][j-1]+1 : 0; // if(dp[i][j] > dp[r][c]) { // r=i; // c=j; // } // } // } // return str1.substring(r-dp[r][c]+1,r+1); //节省空间,每一次的计算只需要上一层的数据,每一格的计算只需要左上角的数据,从右向左计算即可 //固定s1为较长的字符串,s2为较短的字符串 //较短字符串长度的空间 char[] s1,s2; if(str1.length() < str2.length()) { s1 = str2.toCharArray(); s2 = str1.toCharArray(); }else{ s1 = str1.toCharArray(); s2 = str2.toCharArray(); } int[] dp = new int[s2.length]; int flag =0;//记录当前最长公共子串长度; int index = -1;//记录最长公共子串在较长字符串中的结束下标; for(int i=0;i<dp.length;i++) dp[i] = s1[0]==s2[i] ? 1 : 0; for(int i=1;i<s1.length;i++) { for(int j=s2.length-1;j>0;j--) { dp[j] = s1[i]==s2[j] ? dp[j-1]+1 : 0; if(dp[j] > flag) { flag = dp[j]; index = i; } } dp[0] = s1[i]==s2[0] ? 1 : 0; //将第0列单独处理,省去一次判断 } return str1.length() < str2.length() ? str2.substring(index-flag+1,index+1) : str1.substring(index-flag+1,index+1); } }
import java.util.*; public class Solution { /** * longest common substring * @param str1 string字符串 the string * @param str2 string字符串 the string * @return string字符串 */ public String LCS (String str1, String str2) { // write code here int len1 = str1.length(); int len2 = str2.length(); int[][] dp = new int[len1+1][len2+1]; int end = 0; int maxLength = 0; for(int i=1; i<=len1; i++){ for(int j=1; j<=len2; j++){ if(str1.charAt(i-1)==str2.charAt(j-1)){ dp[i][j] = dp[i-1][j-1]+1; }else dp[i][j] = 0; if(maxLength < dp[i][j]){ end = i; maxLength = dp[i][j]; } } } return str1.substring(end-maxLength, end); } }
public static String LCS (String str1, String str2) { // write code here if (str1 == null || str2 == null || str1.equals("") || str2.equals("")) { return "-1"; } String curStr = ""; String maxStr = ""; for (char c : str2.toCharArray()) { curStr += c; if (str1.contains(curStr)) { if (curStr.length() > maxStr.length()) { maxStr = curStr; //如果str1包含当前curStr,并且curStr的长度大于maxStr,那么用curStr替换maxStr。 } } else { //如果不包含,curStr从第一个字符递减,直到str1继续包含目前的curStr while(true) { curStr = curStr.substring(1,curStr.length()); if(str1.contains(curStr)) { break; } } } } return maxStr; }
使用动态规划,优化前:
public static String LCS1 (String str1, String str2) { // write code here int res[][]=new int[str1.length()][str2.length()]; int min=0,maxLen=0; for(int i=0;i<str1.length();i++){ if(str1.charAt(i)==str2.charAt(0)){ res[i][0]=1; }else{ res[i][0]=0; } } for(int i=0;i<str2.length();i++){ if(str2.charAt(i)==str1.charAt(0)){ res[0][i]=1; }else{ res[0][i]=0; } } for(int i=1;i<str1.length();i++){ for(int j=1;j<str2.length();j++){ if(str1.charAt(i)==str2.charAt(j)){ res[i][j]=res[i-1][j-1]+1; if(maxLen<res[i][j]){ maxLen=res[i][j]; min=i; } } } } return maxLen==0?"-1":str1.substring(min-maxLen+1,min+1); }
优化后:
public static String LCS (String str1, String str2) { // write code here if(str1==null||str2==null||str1.length()==0||str2.length()==0){ return "-1"; } int end=0; int maxLen=0; int dp[][]=new int[str1.length()+1][str2.length()+1]; for (int i = 0; i < str1.length(); i++) { for (int j = 0; j < str2.length(); j++) { if(str2.charAt(j)==str1.charAt(i)){ if(i==0||j==0){ dp[i][j]=1; }else{ dp[i][j]=dp[i-1][j-1]+1; } if(dp[i][j]>maxLen){ maxLen=dp[i][j]; end = i; } } } } return maxLen==0?"-1":str1.substring(end-maxLen+1,end+1); }
个人实在不理解通过的python代码,很明显存在漏洞,但不知道为什么编译讷讷通过,只要公共字符串不是以其中的一个字符串开头,编译无法通过
个人认为下面才是正确的
class Solution: def LCS(self,str1 , str2 ): # write code here if len(str1) < len(str2): str1, str2 = str2, str1 res = '' max_len = 0 for i in range(len(str1)): if str1[i - max_len : i+1] in str2: res = str1[i - max_len : i+1] max_len += 1 return res
import java.util.*; public class Solution { public String LCS (String str1, String str2) { int m = str1.length(), n = str2.length(); int[][] dp = new int[m + 1][n + 1]; int max = 0, index = 0; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (str1.charAt(i) == str2.charAt(j)) { dp[i + 1][j + 1] = dp[i][j] + 1; if (max < dp[i + 1][j + 1]) { max = dp[i + 1][j + 1]; index = i + 1; } } } } return max == 0 ? "-1" : str1.substring(index-max,index); } }
public String LCS (String str1, String str2) { // Invariant // lcs[i][j] 代表以str1[i],str2[j]结尾的最长公共子串 // i,j 从1开始 int[][] lcs = new int[str1.length() + 1][str2.length() + 1]; int max = 0; // str1的下标 int index = 0; for (int i = 1; i <= str1.length(); i++) for (int j = 1; j <= str2.length(); j++) { if (str1.charAt(i - 1) == str2.charAt(j - 1)) { lcs[i][j] = lcs[i - 1][j - 1] + 1; if (lcs[i][j] > max) { max = lcs[i][j]; index = i; } } } return str1.substring(index - max, index); }
class Solution { public: /** * longest common substring * @param str1 string字符串 the string * @param str2 string字符串 the string * @return string字符串 */ string LCS(string str1, string str2) { // write code here //string res; int start=0; int i=1; string res; //设置一个小窗,start是开始坐标,i是小窗长度,且i只会增大不会减小。 while(start<str2.size() && start+i <= str2.size()){ string temp(str2,start,i); //将str2以start开始的i个字符用于构造temp if(str1.find(temp) != -1){ i++; res=temp; //如果在str1中找到了小窗内的子串,就把小窗增大 } else{ start++; //如果找不到子串,就将小窗向右移动一格。 } } return res; } };C++
class Solution: def LCS(self , str1 , str2 ): a, b = '', '' for i in str1: a += i if a in str2: b = a else: a = a[1:] return b
import java.util.*; public class Solution { public String LCS (String str1, String str2) { int maxnum=0; int finish=0; int [][] dp=new int[str1.length()][str2.length()]; for(int i=0;i<str1.length();i++) { for(int j=0;j<str2.length();j++) { if(str1.charAt(i)==str2.charAt(j)) { if(i==0||j==0) dp[i][j]=1; else dp[i][j]=dp[i-1][j-1]+1; } if(dp[i][j]>maxnum) { maxnum=dp[i][j]; finish=i; } } } if(maxnum==0)return "-1"; return str1.substring(finish+1-maxnum,finish+1); } }
class Solution: def LCS3(self, str1: str, str2: str): m, n = len(str1), len(str2) top, digit =min(m, n), 0 while top//(10**digit)>10: digit += 1 while digit>=0: longest, index = 0, 0 for length in range(top, 0, -10**digit): for i in range(m - length + 1): if str1[i:i+length] in str2: longest = length index = i break if longest: break top = length+10**digit digit -= 1 return str1[index:index + longest]
class Solution: def LCS(self , str1 , str2 ): # write code here if len(str1) < len(str2): str1, str2 = str2, str1 res = '' maxLen = 0 for i in range(len(str1)): if str1[i - maxLen: i+1] in str2: res = str1[i-maxLen:i+1] maxLen += 1 if len(res) == 0: return -1 return res
class Solution { public: /** * longest common substring * @param str1 string字符串 the string * @param str2 string字符串 the string * @return string字符串 */ string LCS(string str1, string str2) { //+1为了方便计算 [0-1] 的情况 vector<vector<int>> dpVec(str1.size()+1, vector<int>(str2.size()+1, 0)); // 012345678 //"1AB2345CD", //" 12345EF" //endIndex = 6, len = 4 //i1,i2取dpVec位置,取字符时需要-1 int maxLen = 0; vector<int> maxEndIndex{0,0}; for (int i1 = 1; i1 <= str1.size(); i1++) { for (int i2 = 1; i2 <= str2.size(); i2++) { //如果当前位置的字符相等,那么最长公共子串就等于都减去这个字符后的最长长度在+1 if (str1[i1-1] == str2[i2-1]) { dpVec[i1][i2] = dpVec[i1-1][i2-1] + 1; if (dpVec[i1][i2] > maxLen) { maxLen = dpVec[i1][i2]; maxEndIndex = {i1-1,i2-1}; } } else { dpVec[i1][i2] = 0; } } } return str1.substr(maxEndIndex[0] - maxLen + 1, maxLen); } };