题解:洛谷 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. 问题分析:判断两个字符串是否相似,即是否可以通过一次插入、删除或修改操作(或零次,即完全相同)互相转化。这是一个字符串编辑距离的简化问题,核心在于分析字符串长度关系并分类处理。

  2. 算法选择

    • 长度分类:根据长度差将问题分为三种情况:长度相等(修改操作)、长度差 \(1\)(插入/删除操作)、长度差 \(> 1\)(不相似)
    • 双指针匹配:对于长度差 \(1\) 的情况,用双指针逐位比较,允许一次不匹配后跳过较长字符串的一个字符
  3. 关键步骤

    • 读入数据:读取 \(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\)
    • 输出结果similarnot similar
  4. 时间/空间复杂度

    • 时间复杂度:\(O(T \cdot L)\),每组字符串比较为 \(O(L)\)\(L \leq 50\)
    • 空间复杂度:\(O(1)\),仅使用指针和计数器
  5. 编辑距离与双指针的核心思想

    • 操作类型的长度特征:修改操作不改变长度,插入/删除操作改变长度 \(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
posted @ 2026-07-17 15:34  团爸讲算法  阅读(11)  评论(0)    收藏  举报