题解:洛谷 B3958 相似字符串
【题目来源】
洛谷:B3958 [GESP202403 四级] 相似字符串 - 洛谷
【题目描述】
对于两个字符串 \(A\) 和 \(B\),如果 \(A\) 可以通过删除一个字符,或插入一个字符,或修改一个字符变成 \(B\),那么我们说 \(A\) 和 \(B\) 是相似的。
比如 apple 可以通过插入一个字符变成 applee ,可以通过删除一个字符变成 appe ,也可以通过修改一个字符 变成 bpple ,因此 apple 和 applee 、 appe 、 bpple 都是相似的。但 applee 并不能通过任意一个操作变成 bpple ,因此它们并不相似。
特别地,完全相同的两个字符串也是相似的。
给定 \(T\) 组 \(A,B\),请你分别判断他们是否相似。
【输入】
第一行一个正整数 \(T\)。
接下来 \(T\) 行,每行两个用空格隔开的字符串 \(A\) 和 \(B\)。
保证 \(T\le 100\),\(A,B\) 的长度不超过 \(50\)。保证 \(A\) 和 \(B\) 只包含小写字母。
【输出】
输出 \(T\) 行,对于每组 \(A,B\),如果它们相似,则输出 similar,否则输出 not similar。
【输入样例】
5
apple applee
apple appe
apple bpple
applee bpple
apple apple
【输出样例】
similar
similar
similar
not similar
similar
【核心思想】
-
问题分析:判断两个字符串是否相似,即是否可以通过一次插入、删除或修改操作(或零次,即完全相同)互相转化。这是一个字符串编辑距离的简化问题,核心在于分析字符串长度关系并分类处理。
-
算法选择:
- 长度分类:根据长度差将问题分为三种情况:长度相等(修改操作)、长度差 \(1\)(插入/删除操作)、长度差 \(> 1\)(不相似)
- 双指针匹配:对于长度差 \(1\) 的情况,用双指针逐位比较,允许一次不匹配后跳过较长字符串的一个字符
-
关键步骤:
- 读入数据:读取 \(T\) 组字符串 \(A, B\)
- 判定函数 \(isSimilar(A, B)\):
- 若 \(|m - n| > 1\):返回 \(false\)
- 若 \(m = n\):统计不同字符位置数 \(diff\),返回 \(diff \leq 1\)
- 若 \(|m - n| = 1\):
- 确定 \(shorter\) 和 \(longer\)
- 双指针 \(i, j\) 遍历,\(diff\) 记录差异次数
- 若 \(shorter[i] \neq longer[j]\):\(diff \leftarrow diff + 1\),\(j \leftarrow j + 1\)(跳过较长串的一个字符)
- 否则:\(i \leftarrow i + 1, j \leftarrow j + 1\)
- 返回 \(diff \leq 1\)
- 输出结果:
similar或not similar
-
时间/空间复杂度:
- 时间复杂度:\(O(T \cdot L)\),每组字符串比较为 \(O(L)\),\(L \leq 50\)
- 空间复杂度:\(O(1)\),仅使用指针和计数器
-
编辑距离与双指针的核心思想:
- 操作类型的长度特征:修改操作不改变长度,插入/删除操作改变长度 \(1\)。因此长度差 \(> 1\) 时必然不相似,直接排除
- 双指针的容错机制:长度差 \(1\) 时,双指针允许一次不匹配后只移动较长串的指针,模拟"删除较长串的一个字符"或"在较短串插入一个字符"的操作
- 贪心匹配的正确性:从左到右贪心匹配,一旦发现不匹配立即跳过,由于只允许一次操作,这种贪心策略是正确的
- 完全相同的情况:长度相等且 \(diff = 0\) 时也返回相似,满足题目"完全相同的两个字符串也是相似的"要求
- 适用于编辑距离为 \(1\) 的字符串相似性判定、双指针容错匹配类问题
【算法标签】
普及- #字符串入门
【解题思路】

【代码详解】
#include <bits/stdc++.h> // 包含所有标准库头文件
using namespace std; // 使用标准命名空间
// 判断两个字符串是否相似
bool isSimilar(string A, string B) {
int m = A.size(), n = B.size();
// 长度差超过1直接返回不相似
if (abs(m - n) > 1) return false;
// 情况1:长度相等(可能只需要修改一个字符)
if (m == n) {
int diff = 0; // 记录不同字符的数量
for (int i = 0; i < m; i++) {
if (A[i] != B[i]) {
diff++;
if (diff > 1) return false; // 超过1处不同
}
}
return true; // 0或1处不同
}
// 情况2:长度差1(可能需要插入/删除一个字符)
else {
string& shorter = (m < n) ? A : B; // 较短的字符串
string& longer = (m < n) ? B : A; // 较长的字符串
int i = 0, j = 0; // 双指针
int diff = 0; // 差异计数器
while (i < shorter.size() && j < longer.size()) {
if (shorter[i] != longer[j]) {
diff++;
if (diff > 1) return false; // 超过1处差异
++j; // 只移动较长字符串的指针(模拟插入/删除)
} else {
++i;
++j;
}
}
return true; // 最多1处差异
}
}
int main() {
int T; // 测试用例数量
cin >> T;
while (T--) {
string A, B;
cin >> A >> B; // 输入两个字符串
if (isSimilar(A, B)) {
cout << "similar" << endl;
} else {
cout << "not similar" << endl;
}
}
return 0;
}
【运行结果】
5
apple applee
similar
apple appe
similar
apple bpple
similar
applee bpple
not similar
apple apple
similar
浙公网安备 33010602011771号