题解 | #最长公共子串#
最长公共子串
http://www.nowcoder.com/practice/f33f5adc55f444baa0e0ca87ad8a6aac
题意:
方法一:暴力法
- 对于str1和str2的最长公共子串,最直接的办法就是穷举他们的子串并判断是否是公共拥有的。
- 思路:(1)穷举两字符串起始位置 (2)寻找以该起始位置开始的公共子串,判断是否是最长的并更新
代码如下:
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
//startIndex是最长公共子串在str1中的开始位置,maxLen是长度
int startIndex=0,maxLen=0;
int len1=str1.size(),len2=str2.size();
//双重循环,分别是str1的开始位置和str2的开始位置
for(int start1=0;start1<len1;start1++){
for(int start2=0;start2<len2;start2++){
int i=start1,j=start2,len=0;
//判断字符串是否相等,以及长度
while(i<len1&&j<len2&&str1[i++]==str2[j++])
len++;
//超过当前最大长度,更新startIndex和maxLen
if(len>maxLen){
startIndex=start1;
maxLen=len;
}
}
}
//返回最长公共子串
return str1.substr(startIndex,maxLen);
}
};
复杂度分析:
时间复杂度:,双重循环穷举起始位置,while循环寻找公共子串。其中m,n分别是两字符串的长度,且n<m。
空间复杂度:,仅常数个临时变量。
方法二:动态规划
- 方法一中的解法存在很多子问题重叠的情况,有较大的改进空间。基于此,可以使用动态规划的方法,寻找最优子结构并用额外空间保存子问题的解,以此来避免在解决同样的问题上花费时间。
- 思路:二维数组dp[i][j],表示在str1中以位置i结尾,和在str2中以位置j结尾的最长公共子串的长度。有如下递推式:
(1) if str1[i]!=str2[j] -> dp[i][j]=0
(2) if str1[i]==str2[j] -> dp[i][j]=dp[i-1][j-1]+1 - 式(1)显然,若两字符串结尾的字符不等,则最长公共子串长度为0。式(2)表示两字符串结尾字符相等,则依赖于删除该相等的字符之后的两字符串的最长公共子串。如下是具体例子的图解以方便理解。
图解如下:
如下是数组dp的产生过程,其中str1="1AB2345CD", str2="12345EF".
代码如下:
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
int len1=str1.size(),len2=str2.size();
//endIndex是最长公共子串在str1中的结束位置
int endIndex=-1,maxLen=0;
//二维数组dp,其内的值默认为0。维度为(len1+1)*(len2+1)
//其中dp[0][?]和dp[?][0]为辅助的单元,用于递推式的起始条件,相比于上述递推式有所变化。
vector<vector<int>> dp(len1+1,vector<int>(len2+1));
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
//相等则更新dp[i][j],否则仍然为0
if(str1[i-1]==str2[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
//更新maxLen和endIndex
if(dp[i][j]>maxLen){
maxLen=dp[i][j];
endIndex=i-1;
}
}
}
}
int startIndex=endIndex-maxLen+1;
return str1.substr(startIndex,maxLen);
}
};
复杂度分析:
时间复杂度:,双重for循环遍历dp数组。其中n,m为两字符串的长度。
空间复杂度:,dp数组维度为n*m。