1. 问题背景与需求解析
这道题目源自华为技术岗位的编程能力考核,主要考察应聘者对字符串处理算法的掌握程度。在实际开发中,字符串匹配是高频出现的场景,比如日志分析、数据清洗、文本比对等场景都需要用到类似技术。
题目要求找出两个字符串中最长的公共连续子串。注意是连续子串(substring)而非子序列(subsequence)。举个例子:
- 字符串A:"abcdefg"
- 字符串B:"bcdxyz"
最长公共子串是"bcd",长度为3
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 算法思路分析
2.1 暴力解法
最直观的方法是双重循环遍历所有可能的子串组合:
- 遍历字符串A的所有子串
- 对每个子串检查是否存在于字符串B中
- 记录最长的匹配子串
时间复杂度O(n³),在字符串较长时性能较差。
2.2 动态规划解法
更优的方案是使用动态规划(DP)。我们定义一个二维数组dp,其中dp[i][j]表示以A[i]和B[j]结尾的最长公共子串长度。
状态转移方程:
code复制如果 A[i] == B[j]:
dp[i][j] = dp[i-1][j-1] + 1
否则:
dp[i][j] = 0
这样可以将时间复杂度优化到O(n²)。
3. 代码实现详解
3.1 Python实现
python复制def longest_common_substring(a, b):
m, n = len(a), len(b)
dp = [[0]*(n+1) for _ in range(m+1)]
max_len = 0
end_pos = 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_len:
max_len = dp[i][j]
end_pos = i
else:
dp[i][j] = 0
return a[end_pos-max_len:end_pos] if max_len > 0 else ""
3.2 关键点说明
- dp数组大小设为(m+1)×(n+1)是为了处理边界条件
- 使用max_len和end_pos记录最长子串的位置信息
- 最后返回子串时要注意切片范围
4. 性能优化技巧
4.1 空间优化
可以观察到dp[i][j]只依赖于dp[i-1][j-1],因此可以将二维数组优化为一维:
python复制def longest_common_substring_opt(a, b):
m, n = len(a), len(b)
dp = [0] * (n + 1)
max_len = 0
end_pos = 0
for i in range(1, m+1):
prev = 0
for j in range(1, n+1):
temp = dp[j]
if a[i-1] == b[j-1]:
dp[j] = prev + 1
if dp[j] > max_len:
max_len = dp[j]
end_pos = i
else:
dp[j] = 0
prev = temp
return a[end_pos-max_len:end_pos] if max_len > 0 else ""
4.2 其他优化思路
- 可以先比较两个字符串长度,选择较短的作为内层循环
- 使用KMP算法思想进行优化
- 对于特别长的字符串可以考虑后缀自动机方法
5. 常见错误与调试技巧
5.1 典型错误
- 数组越界:忘记处理边界条件
- 初始化错误:dp数组未正确初始化
- 索引混淆:字符串索引与dp数组索引对应关系搞错
5.2 调试建议
- 打印dp矩阵观察中间结果
- 对短字符串手动计算验证
- 特别注意空字符串和完全无匹配的情况
6. 实际应用场景
- 文件差异比较工具实现
- DNA序列比对
- 代码抄袭检测
- 搜索引擎中的相似文档发现
- 版本控制系统中的变更分析
7. 扩展思考
- 如何找出所有最长公共子串而不仅是一个?
- 如果允许少量不匹配(模糊匹配)该如何修改算法?
- 如何扩展到多个字符串的最长公共子串查找?
提示:在实际面试中,除了写出正确代码,还需要能够分析时间/空间复杂度,并讨论可能的优化方案。
