----------------------------------------------本题链接----------------------------------------------
题目描述
给定两个字符串str1和str2,输出两个字符串的最长公共子串,如果最长公共子串为空,输出-1。
示例
输入
"1AB2345CD","12345EF"
返回值
"2345"
思路
动态规划问题
假设两个字符串长度为n、m,具体分析其问题:
- 最优子结构:如果知道 n-1、m-1 长度字符串的最长公共子串s,则 n、m 长度字符串最长公共子串为 s+1(当最后字符相同时) 或者 s(当最后字符不同时)
- 重叠子问题:为确定 n、m 长度字符串最长公共子串,需要知道n-1、m-1 长度字符串的最长公共子串;为确定 n-1、m-1 长度字符串最长公共子串,需要知道n-2、m-2 长度字符串的最长公共子串。。。
- 状态转移方程:
d p ( n , m ) = { 0 , n = 0 o r m = 0 d p ( n − 1 , m − 1 ) + 1 , n > 0 & m > 0 dp(n, m)=\left\{ \begin{aligned} & 0 ,& n=0 \quad or \quad m =0\\ & dp(n-1,m-1) + 1 ,& n>0 \quad \& \quad m > 0 \\ \end{aligned} \right. dp(n,m)={0,dp(n−1,m−1)+1,n=0orm=0n>0&m>0
如何列出正确的状态转移方程:
- 确定「base case」: 某一字符串为空时,匹配不到公共子串,返回0
- 确定「状态」 (也就是原问题和子问题中会变化的变量):两个字符串的长度会不断向 base case 靠近,所以「状态」就是两个字符串的长度
- 确定「选择」(也就是导致「状态」产生变化的行为):我们每拿出一个字符进行匹配,就越离我们的目标找到最长公共子串越接近。
- dp数组的定义:输入两个字符串长度,返回当前最长公共子串长度
推荐大佬总结的动态规划问题:labuladong的算法小抄
算法过程
- 判断异常情况
- 初始化dp数组
- 做选择
- 满足条件则更新dp数组,同时记录最长公共子串长度和初始位置
- 确定最长公共子串
解答
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) {
if(str1 == null || str2 == null || str1.equals("") || str2.equals(""))
return "-1";
int m = str1.length(), n = str2.length(), res = 0, pos = 0;
// 初始化dp
int[][] dp = new int[m+1][n+1];
for(int i = 1; i <= m; i++){
char c1 = str1.charAt(i-1);
for(int j = 1; j <= n; j++){
char c2 = str2.charAt(j-1);
if(c1 == c2){
dp[i][j] = dp[i-1][j-1] + 1;
if(dp[i][j] > res){
res = dp[i][j];
pos = i;
}
}
}
}
return res == 0 ? "-1" : str1.substring(pos - res, pos);
}
}