Longest Common Substring

本文介绍了一种使用动态规划解决字符串匹配问题的方法,特别关注于找到两个字符串之间的最长公共子串。通过逐步构建状态转移矩阵,我们能够高效地计算出所需答案,避免了暴力枚举带来的高时间复杂度。

Given two strings, find the longest common substring.
Return the length of it.

Note
The characters in substring should occur continiously in original string. This is different with subsequnce.
Example
Given A=“ABCD”, B=“CBCE”, return 2


很著名的LCS问题。我们需要得到最大长度的公共子串的长度。如果暴力破解的话,首先得枚举出A所有的子串和B所有的子串,再根据比较得到最大长度的公共子串的长度,复杂度太高。
因此想到DP解法。
这里dp[i][j]可不是表示A的前i个字符与B的前j个字符的LCS。因为substring是连续的,对于这种连续的问题DP实际是记录当前元素“取或不取”。
所以dp[i][j]的含义是含有A中第i个字符和B中第j个字符的LCS。
dp[i][j] = dp[i - 1][j - 1] + 1 if A[i] == B[j]
dp[i][j] = 0 if A[i] != B[j]

public class Solution {
    /**
     * @param A, B: Two string.
     * @return: the length of the longest common substring.
     */
    public int longestCommonSubstring(String A, String B) {
        // write your code here
        int[][] dp = new int[A.length() + 1][B.length() + 1];
        for(int i = 1; i < dp.length; i++){
            for(int j = 1; j < dp[0].length; j++){
                if(A.charAt(i - 1) == B.charAt(j - 1)){
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }else{
                    dp[i][j] = 0;
                }
            }
        }
        int max = 0;
        for(int i = 0; i < dp.length; i++){
            for(int j = 0; j < dp[0].length; j++){
                max = Math.max(max, dp[i][j]);
            }
        }
        return max;
    }
}
<think>好的,我现在需要详细解释最长公共子串(Longest Common Substring)的定义和实现方法。首先,得明确最长公共子串和最长公共子序列(Longest Common Subsequence)的区别,避免混淆。最长公共子串要求字符连续,而子序列不需要。比如,"abcde"和"abfce"的公共子串是"ab"或"ce",而最长公共子序列可能是"abce"。 接下来,用户需要了解具体的实现方法。根据提供的引用资料,动态规划是常用的方法。我需要先解释动态规划的基本思路,然后逐步构建解决方案。比如,动态规划表的结构,如何填充表格,以及如何通过表格回溯找到最长子串。 然后,具体步骤可能需要详细展开。例如,创建一个二维数组dp,其中dp[i][j]表示以字符串a的第i个字符和字符串b的第j个字符结尾的最长公共子串的长度。当a[i-1]等于b[j-1]时,dp[i][j] = dp[i-1][j-1] + 1,否则为0。同时,记录最大值和结束位置,以便最后提取子串。 需要检查引用的不同语言的实现,确保算法的一致性。比如引用3和4中的Java和C++代码都使用了类似的动态规划方法,二维数组的处理方式也相似。此外,引用5中提到优化思路,即不需要存储整个二维数组,只需要前一行的数据即可,这样可以优化空间复杂度,但可能用户的问题暂时不需要深入优化,所以可能放在后面作为补充。 另外,用户可能需要时间复杂度分析。动态规划方法的时间复杂度是O(m*n),其中m和n是两个字符串的长度。空间复杂度如果使用二维数组的话也是O(m*n),但可以优化到O(n)或O(m)如果只保留一行或一列的数据。 最后,要生成相关问题,参考用户之前的例子,比如如何优化空间复杂度,不同实现方式的比较,或者应用场景等。这些问题应该与最长公共子串相关,并且能够帮助用户进一步学习。 需要注意用户提供的引用中有一些代码示例,比如JavaScript和Objective-C的实现,可能需要提及不同语言实现的差异,但用户主要问题可能更关注算法本身,所以重点放在算法步骤而不是具体代码。 总结回答结构:先定义,然后动态规划步骤,时间复杂度分析,代码示例(选择Python作为例子,因为用户提供的示例中有Python代码),最后应用场景和相关问题。</think>### 最长公共子串(Longest Common Substring)定义与实现 #### 定义 最长公共子串是指两个或多个字符串中**连续相同**的最长子序列。例如,字符串 $s_1 = \text{"abcde"}$ 和 $s_2 = \text{"abfce"}$ 的最长公共子串是 $\text{"ab"}$ 或 $\text{"ce"}$(长度均为2)。与最长公共子序列(LCS)不同,公共子串要求字符必须连续[^3][^5]。 --- #### 动态规划实现方法 ##### 核心思想 通过构建二维数组 $dp[i][j]$,记录以字符串 $a$ 的第 $i$ 个字符和字符串 $b$ 的第 $j$ 个字符结尾的最长公共子串长度。递推公式为: $$ dp[i][j] = \begin{cases} dp[i-1][j-1] + 1 & \text{if } a[i-1] = b[j-1] \\ 0 & \text{otherwise} \end{cases} $$ 同时,记录全局最大值 $max\_length$ 及其结束位置 $index$,以便提取子串[^3][^4][^5]。 ##### 步骤详解 1. **初始化二维数组**:第一行和第一列设为0,简化边界条件。 2. **填充数组**:遍历两字符串的每个字符,若字符相等,则当前值等于左上角值加1。 3. **更新最大值**:每次计算 $dp[i][j]$ 后,更新全局最大值和索引。 4. **提取结果**:根据记录的索引和长度,从原字符串中截取子串。 ##### 时间复杂度与空间复杂度 - **时间复杂度**:$O(m \times n)$($m,n$ 为字符串长度) - **空间复杂度**:$O(m \times n)$,可优化至 $O(\min(m,n))$[^5]。 --- #### Python实现示例 ```python def longest_common_substring(a, b): m, n = len(a), len(b) dp = [[0] * (n+1) for _ in range(m+1)] max_length = 0 end_index = 0 for i in range(1, m+1): for j in range(1, n+1): if a[i-1] == b[j-1]: dp[i][j] = dp[i-1][j-1] + 1 if dp[i][j] > max_length: max_length = dp[i][j] end_index = i else: dp[i][j] = 0 return a[end_index - max_length : end_index] if max_length > 0 else "" ``` --- #### 应用场景 1. **DNA序列比对**:寻找生物序列中的相似片段。 2. **文本相似度分析**:检测抄袭或重复内容。 3. **版本控制系统**:识别代码修改的公共部分[^1][^2][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值