描述
给定两个字符串str1和str2,输出两个字符串的最长公共子串
题目保证str1和str2的最长公共子串存在且唯一。
数据范围: 1≤∣str1∣,∣str2∣≤50001 \le |str1|,|str2| \le 5000 1≤∣str1∣,∣str2∣≤5000
要求: 空间复杂度 O(n2)O(n^2)O(n2),时间复杂度 O(n2)O(n^2)O(n2)
示例1
输入: “1AB2345CD”,“12345EF”
返回值: “2345”
备注:
1≤∣str1∣,∣str2∣≤5 0001 \leq |str_1|, |str_2| \leq
5,0001≤∣str1∣,∣str2∣≤5000
注意这题求的是最长公共子串,不是最长公共子序列,子序列可以是不连续的,但子串一定是连续的。
定义dp[i][j]表示字符串str1中第i个字符和str2种第j个字符为最后一个元素所构成的最长公共子串。如果要求dp[i][j],也就是str1的第i个字符和str2的第j个字符为最后一个元素所构成的最长公共子串,我们首先需要判断这两个字符是否相等。
如果不相等,那么他们就不能构成公共子串,也就是
dp[i][j]=0;
如果相等,我们还需要计算前面相等字符的个数,其实就是dp[i-1][j-1],所以
dp[i][j]=dp[i-1][j-1]+1;
具体做法:
step 1:我们可以用dp[i][j]dp[i][j]dp[i][j]表示在str1中以第iii个字符结尾在str2中以第jjj个字符结尾时的公共子串长度,
step 2:遍历两个字符串填充dp数组,转移方程为:如果遍历到的该位两个字符相等,则此时长度等于两个前一位长度+1,dp[i][j]=dp[i−1][j−1]+1dp[i][j] = dp[i - 1][j - 1] + 1dp[i][j]=dp[i−1][j−1]+1,如果遍历到该位时两个字符不相等,则置为0,因为这是子串,必须连续相等,断开要重新开始。
step 3:每次更新dp[i][j]dp[i][j]dp[i][j]后,我们维护最大值,并更新该子串结束位置。
step 4:最后根据最大值结束位置即可截取出子串。
转移方程为:如果遍历到的该位两个字符相等,则此时长度等于两个前一位长度+1,dp[i][j]=dp[i−1][j−1]+1,如果遍历到该位时两个字符不相等,则置为0,因为这是子串,必须连续相等,断开要重新开始。
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
//1、用dp[i][j]表示str1中第i字符结尾,在str2中以第j字符结尾时公共子串长度
int[][] dp = new int[str1.length() + 1][str2.length() + 1];
//2、定义整型max,pos,并赋值为0
int max = 0;
int pos = 0;
//3、遍历两个字符串填充dp数组
for (int i = 1; i <= str1.length(); i++) {
for (int j = 1; j <= str2.length(); j++) {
//4、如果该两位相同
if (str1.charAt(i - 1) == str2.charAt(j - 1))
//5、则此时长度等于两个前一位长度+1,
dp[i][j] = dp[i - 1][j - 1] + 1;
//6、否则
else
//7、该位置为0,这是子串,必须连续相等,断开要重新开始
dp[i][j] = 0;
//8、更新最大长度.如果dp大于最大,将dp赋值给最大,pos在i-1
if (dp[i][j] > max) {
max = dp[i][j];
pos = i - 1;
}
}
}
//9、最后根据最大值结束位置即可截取出子串
return str1.substring(pos - max + 1, pos + 1);
}
}