最长公共子串递归记忆化代码测试用例失败问题排查与修正
修正最长公共子串的递归记忆化实现
首先明确:最长公共子串是连续的字符序列,和子序列逻辑不同。你的迭代DP能正常工作,说明状态转移逻辑是对的,递归记忆化的问题大概率出在状态定义、记忆化缓存的使用或者递归流程上。
常见递归记忆化错误原因
- 错误地将记忆化状态存储全局最大值,而非以当前位置结尾的公共子串长度
- 边界条件处理遗漏(比如其中一个字符串遍历完时,长度为0)
- 递归过程中没有正确传递或更新全局最长长度
- 重复递归调用导致的性能问题(甚至超时)
修正后的递归记忆化C++代码
假设你的迭代DP代码是基于二维数组dp[i][j]表示以s1[i-1]和s2[j-1]结尾的最长公共子串长度,状态转移为:
if (s1[i-1] == s2[j-1]) dp[i][j] = dp[i-1][j-1] + 1; else dp[i][j] = 0;
对应的递归记忆化实现如下:
#include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; // 记忆化缓存,dp[i][j]表示以s1[i]和s2[j]结尾的最长公共子串长度 vector<vector<int>> memo; int max_len = 0; int dfs(const string& s1, const string& s2, int i, int j) { // 边界条件:遍历到字符串开头之前,返回0 if (i < 0 || j < 0) return 0; // 如果已经计算过当前状态,直接返回缓存值 if (memo[i][j] != -1) return memo[i][j]; int current = 0; if (s1[i] == s2[j]) { // 当前字符匹配,递归计算前一个位置的结果+1 current = dfs(s1, s2, i-1, j-1) + 1; // 更新全局最长长度 max_len = max(max_len, current); } else { // 当前字符不匹配,当前长度为0,同时需要递归检查两个方向(因为最长子串可能在其他位置) dfs(s1, s2, i-1, j); dfs(s1, s2, i, j-1); current = 0; } // 缓存当前状态的结果 memo[i][j] = current; return current; } int longestCommonSubstring(const string& s1, const string& s2) { int n = s1.size(); int m = s2.size(); max_len = 0; // 初始化记忆化数组,-1表示未计算 memo.assign(n, vector<int>(m, -1)); // 从两个字符串的末尾开始递归 dfs(s1, s2, n-1, m-1); return max_len; } int main() { int n, m; cin >> n >> m; string s1, s2; cin >> s1 >> s2; cout << longestCommonSubstring(s1, s2) << endl; return 0; }
关键修正点说明
- 记忆化状态定义:
memo[i][j]严格对应迭代DP中的dp[i+1][j+1],存储以s1[i]和s2[j]结尾的最长公共子串长度,而非全局最大值,确保缓存能被重复利用。 - 边界处理:当
i<0或j<0(即超出字符串范围)时,直接返回0,符合迭代DP中dp[0][*]和dp[*][0]为0的逻辑。 - 不匹配时的递归:即使当前字符不匹配,仍需递归检查
s1[i-1]与s2[j]、s1[i]与s2[j-1]的情况,避免遗漏其他位置的公共子串。 - 全局最大值跟踪:在每次匹配成功时更新
max_len,确保能捕获到所有可能的最长子串长度。
针对你的测试用例验证
对于输入:
17 60 KXCGMTMVVGFQQWSPD JXZADDUKVLQPKUZJZHWSUTPCAFSYAIBJHAMMEGWBTPQELRNKBLDXGUZGCSEC
修正后的递归代码会正确计算出最长公共子串的长度,不会出现之前的失败情况。
内容的提问来源于stack exchange,提问作者Prashant Rai
相关产品推荐
相关产品推荐

