给定两个字符串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);
}
} 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;
} 个人实在不理解通过的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);
}
} 此题与前面的最长公共子序列的区别在于:公共子串是连续的,而公共子序列不要求连续,只要求先后顺序。
因此本题可以直接从str1/str2中截取相关部分作为结果,进而dp数组也可以定义为int[][]型,只记录下标信息。
并且int[][]的默认值也刚好完成边界值的初始化,循环可以从1开始。
import java.util.*;
public class Solution {
public String LCS (String str1, String str2) {
int n1 = str1.length();
int n2 = str2.length();
int[][] dp = new int[n1+1][n2+1];
int end = 0;
int maxLength = 0;
for (int i = 1; i <= n1; i++) {
for (int j = 1; j <= n2; j++) {
if (str1.charAt(i-1) == str2.charAt(j-1)) {
dp[i][j] = dp[i-1][j-1] + 1;
}
if (dp[i][j] > maxLength) { // 更新结果
maxLength = dp[i][j];
end = i;
}
}
}
return str1.substring(end-maxLength, end);
}
}
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);
}
}; class Solution: def LCS(self , str1 , str2 ): dp = [ [''] * (len(str2) + 1) for _ in range(len(str1) + 1)] ans = '' for i in range(1, len(str1)+1): for j in range(1, len(str2)+1): if str1[i-1] == str2[j-1]: dp[i][j] = dp[i-1][j-1] + str1[i-1] ans = dp[i][j] if len(dp[i][j]) >= len(ans) else ans else: dp[i][j] = '' return ans
import java.util.*; public class Solution { /** * longest common substring * @param str1 string字符串 the string * @param str2 string字符串 the string * @return string字符串 */ //状态记录,取缔二维数组,减少内存开销 int start = 0; int stepNum = 0; int currentStart = 0; int currentStepNum = 0; boolean cancleLeft = false; boolean cancleRight = false; int len2 = 0; public String LCS (String str1, String str2) { String breakJudge; if (str1.length() < str2.length()){ String temp = str1; str1 = str2; str2 = temp; } len2 = str2.length(); for (int i = 0;i < str1.length() && !cancleRight;i++){ for (int j = 0; j < str2.length() && i + j < str1.length();j++) doJudge(str1,str2,j + i,j); clear(); if (!cancleRight) cancleRight = breakJudge(str1.length(),i); } clear(); for (int i = 0;i < str2.length() && ! cancleLeft;i++){ for (int j = 0; i + j < str2.length();j++) doJudge(str1,str2,j,j + i); clear(); if (!cancleLeft) cancleLeft = breakJudge(str2.length(),i); } return stepNum == 0 ? "-1" : str1.substring(start,start + stepNum); } // 判断能否提前退出 public boolean breakJudge(int len,int i){ if (stepNum == len2 || (stepNum >= (len - i))){ return true; } return false; } public void clear(){ if (currentStepNum > stepNum){ stepNum = currentStepNum; start = currentStart; } currentStepNum = 0; currentStart = 0; } public void doJudge(String str1,String str2,int index1,int index2){ if ( str1.charAt(index1) == str2.charAt(index2)){ if (currentStepNum == 0)// 记录步长为0 currentStart = index1; //更新起点 currentStepNum ++;//步长加一 } else clear();//不等,对比当前步长与缓存步长,更新保存的步长与起点 } }