Longest Common Substring 最長公共子字符串
動態規划問題
動態規划問題的兩個特點:
1.最優子結構
2.重疊子問題
因為有重疊子問題,當前計算的過程中可能有的問題在之前的計算已經計算過了,現在又要計算一遍,導致大量重復的計算。
動態規划通過找到解決問題的遞推關系,將已經完成計算的存儲起來,
當開始新的計算時如果包含之前計算的子問題時,不需要再次計算,只需要訪問已經存儲的計算結果就可以,
這種方法減少了時間復雜度,增加了存儲空間。
假設有兩個字符串s[0,...m],t[0,...,n],求兩個字符串的最長公共子字符串
定義矩陣mXn的矩陣L,L[i][j]表示以s[i]開始和t[j]結尾的公共子字符串長度的最大值
那么對於L[i+1][j+1]只是比L[i][j]增加了s[i+1]和t[j+1]
因此可以構造出最長公共子字符串的遞歸式:
if s[i]==t[j]
L[i][j]=L[i-1][j-1]+1
if s[i]!=t[j]
L[i][j]=0
假設有兩個字符串:"ABAB"和"BABA" ,構造出了上述的矩陣

代碼實現
public static String LCS(String s1,String s2){
if(s1.isEmpty() || s2.isEmpty()){
return "";
}
int indexMax=0,maxn=0;
int[][] L=new int[s1.length()][s2.length()];
for(int i=0;i<s1.length();i++){
for(int j=0;j<s2.length();j++){
if(s1.charAt(i)==s2.charAt(j)){
if(i==0 || j==0){
L[i][j]=1;
}else{
L[i][j]=L[i-1][j-1]+1;
}
}
if(L[i][j]>maxn){
maxn=L[i][j];
indexMax=i;
}
}
}
return s1.substring(indexMax+1-maxn, indexMax+1);
}
算法分析:
時間復雜度:O(m*n)
空間復雜度:O(m*n)
算法優化
從上面動態查找最長公共子字符串的過程中發現,在循環查找的過程中只會用到矩陣L中的兩行,即正在計算的一行和完成計算的上一行,之前計算的和帶計算的都用不到,
所以只需要維護兩行數據就足夠了,不需要使用mxn的數組
代碼實現:
public class LCS_improve {
public static String LCS_improve(String s1,String s2){
if(s1.isEmpty() || s2.isEmpty()){
return "";
}
int indexMax=0,maxn=0;
int [][] L=new int[2][s1.length()];
for(int i=0;i<s1.length();i++){
int cur=(i+2)%2;
int pre=(i+1)%2;
for(int j=0;j<s2.length();j++){
if(s1.charAt(i)==s2.charAt(j)){
if(i==0 || j==0){
L[cur][j]=1;
}else{
L[cur][j]=L[pre][j-1]+1;
}
}else{
L[cur][j]=0;
}
if(L[cur][j]>maxn){
maxn=L[cur][j];
indexMax=i;
}
}
}
return s1.substring(indexMax+1-maxn, indexMax+1);
}
}
算法分析:
時間復雜度:O(mn)
空間復雜度:O(min(m,n))
