LeetCode 161. 相隔为 1 的编辑距离
题目描述
题意分析
判断
s与t能否通过恰好一次插入、删除或替换变成相同字符串。完全相同的两个字符串编辑距离为 0,所以应返回false。一次编辑最多改变一个字符的长度,因此长度差大于 1 时不可能满足。长度相等时只能用一次替换;长度相差 1 时,只能向较短串插入字符,等价于从较长串删除字符。
这个限制使我们不需要计算完整编辑距离:从左往右比较,首次不同时,根据原始长度关系唯一确定应跳过哪一边。后续部分必须完全匹配,否则一次编辑不够。
解法:双指针统计唯一编辑
核心思路
[!blue]
i、j分别指向两串下一个待匹配的位置,edits记录已经使用的编辑次数。开始时两个位置都为 0,也没有消耗编辑。若两边都有字符且相等,同时前进。若不同,则必须用一次编辑处理当前位置:等长时同时跳过两个字符,表示替换;
s较长时只跳过s[i],表示删除;t较长时只跳过t[j],表示插入到s。为什么不需要尝试其他跳法?首次失配以前的前缀已经相同;剩下只有一次操作,而且长度差已经确定操作类型。等长串用插入或删除会产生无法修复的长度差,不等长串用替换则无法消除长度差。因此每次移动都有唯一依据。
循环条件使用
i < m || j < n,只要一边没结束就继续。这让尾部多出来的字符也进入失配分支,消耗一次编辑;读取字符前则分别检查下标,避免越界。第二次失配立刻返回false,最终仅在edits == 1时返回true。
解题步骤
- 记录原长度
m、n,长度差超过 1 则直接返回false。- 初始化
i = 0、j = 0、edits = 0。- 当前字符相同,就同时推进两个指针。
- 否则增加编辑次数,超过 1 则失败;按长度关系推进较长串的指针,等长时推进两边。
- 两串均扫描完后,检查是否恰好使用了一次编辑。
对
"ab"与"acb",匹配a后遇到b、c,只推进较长串跳过c,随后两个b匹配。对空串与单字符字符串,循环直接把唯一字符计为一次编辑;两个空串则一次也不编辑,返回false。
代码实现
class Solution {
public boolean isOneEditDistance(String s, String t) {
int m = s.length();
int n = t.length();
if (Math.abs(m - n) > 1) {
return false;
}
int i = 0;
int j = 0;
int edits = 0;
while (i < m || j < n) {
if (i < m && j < n && s.charAt(i) == t.charAt(j)) {
i++;
j++;
continue;
}
if (++edits > 1) {
return false;
}
if (m > n) {
// 删除 s[i]
i++;
} else if (m < n) {
// 向 s 插入 t[j]
j++;
} else {
// 替换 s[i] 为 t[j]
i++;
j++;
}
}
return edits == 1;
}
}
func isOneEditDistance(s string, t string) bool {
m, n := len(s), len(t)
if m-n > 1 || n-m > 1 {
return false
}
i, j, edits := 0, 0, 0
for i < m || j < n {
if i < m && j < n && s[i] == t[j] {
i++
j++
continue
}
edits++
if edits > 1 {
return false
}
if m > n {
// 删除 s[i]
i++
} else if m < n {
// 向 s 插入 t[j]
j++
} else {
// 替换 s[i] 为 t[j]
i++
j++
}
}
return edits == 1
}
复杂度分析
- 时间复杂度:$O(m+n)$,两个指针始终向右移动,每轮至少推进一个;长度差超过 1 时直接结束。
- 空间复杂度:$O(1)$,只保存长度、位置和编辑计数,不创建字符数组或后缀子串。
关键点总结
[!green]
- 使用原始长度差判断操作类型,第一次跳过后无需重新计算后缀长度。
- 失配时只推进被删除或被插入一侧的指针,另一侧仍需参与下一轮匹配。
edits == 1同时排除了完全相同与需要多次编辑的字符串。- Java 按
char、Go 按字节比较;这里沿用本篇题意中的 ASCII 字符输入范围。
易错点总结
[!yellow]
- 返回
edits <= 1:会把两个完全相同的字符串误判为符合要求。- 失配后一律同时推进:长度不等时应保留较短串当前位置,否则会错过仍可匹配的字符。
- 只扫描到较短串结尾:可能漏掉较长串末尾的唯一额外字符,需要另行处理;当前
||循环已把它统一计入。- 在检查下标前读取字符:一边结束而另一边仍有字符是合法边界,必须先做范围检查。
- 通过复制后缀比较却仍标为常数空间:这类实现可能创建线性大小的中间字符串,当前双指针没有该开销。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 72. 编辑距离 | 中等 | 原题求任意编辑距离,本题只验证距离1,可用长度关系直接决定指针移动。 |
| 680. 验证回文串 II | 简单 | 同样在首次失配处处理一次操作,原题只允许删除并要求回文,本题还允许插入和替换。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!