Levenshtein距离只需两行DP数组,因dpi仅依赖dpi-1、dpi、dpi-1;用两个一维vector滚动更新可实现O(min(m,n))空间复杂度,需正确初始化边界并处理UTF-8多字节字符陷阱。

Levenshtein 距离为什么只需要两行 DP 数组
因为状态转移只依赖上一行和当前行的左侧值,dp[i][j] 仅由 dp[i-1][j](删)、dp[i][j-1](增)、dp[i-1][j-1](替)决定。整张二维表按行滚动计算时,存满两行足矣——旧的“上一行”在算完当前行后就可被覆盖。
如何用 vector> 实现空间 O(min(m,n)) 的滚动更新
实际不需要固定两行,只需两个一维 vector<int></int>:一个叫 prev(上一行),一个叫 curr(当前行)。为最小化空间,先确保 s1 是较短串,让 prev 和 curr 长度为 s1.length() + 1;否则交换输入顺序再算。
常见错误现象:std::out_of_range 或结果偏大,多因未正确初始化首行/首列,或错把 s1 当长串导致数组越界。
-
prev[j]初始为j(空串转 s1[0..j-1] 需 j 次插入) - 循环中,
curr[0]始终为i(s1 转空串需 i 次删除) - 字符相等时,
curr[j] = prev[j-1];否则取三者最小值加 1
避免拷贝开销的 in-place 优化写法
不用每次 swap(prev, curr) 后清空 curr,而是复用同一块内存,用指针切换语义:
立即学习“C++免费学习笔记(深入)”;
vector<int> row0(n + 1), row1(n + 1);
vector<int> *prev = &row0, *curr = &row1;
for (int i = 1; i <= m; ++i) {
(*curr)[0] = i;
for (int j = 1; j <= n; ++j) {
int cost = (s1[j-1] == s2[i-1]) ? 0 : 1;
(*curr)[j] = min({(*prev)[j] + 1, (*curr)[j-1] + 1, (*prev)[j-1] + cost});
}
swap(prev, curr); // 下轮 prev 指向刚算出的 curr
}
return (*prev)[n]; // 注意:最后 swap 过一次,结果在 prev 中
性能影响:省去 vector resize 和元素拷贝,对长字符串(如 >10k 字符)差异明显;兼容性无问题,C++11 起均可运行。
边界 case 和字符类型陷阱
Levenshtein 本身不关心字符编码,但 C++ 中若传入 std::string 含 UTF-8 多字节字符(如中文),直接按字节比会出错——此时 s1[j-1] == s2[i-1] 比的是单个字节而非完整码点。
使用场景若涉及非 ASCII 文本,必须先转为 Unicode 码点序列(如用 std::u32string 或第三方库解析 UTF-8),否则距离计算无意义。
容易被忽略的地方:当任一输入为空串时,返回另一串长度即可,不必进主循环;但若提前没判空,row0 或 row1 构造时 n+1 为 1,仍能跑通——只是多做了几轮无意义迭代。


















