LeetCode-Go 题解:859. Buddy Strings(亲密字符串)——两种情况的分类讨论与 Go 实现
2026/9/12 1:20:49 网站建设 项目流程

LeetCode-Go 题解:859. Buddy Strings(亲密字符串)——两种情况的分类讨论与 Go 实现

【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go

导读

本文以 leetcode/0859.Buddy-Strings/README.md 为骨架,完整解析 LeetCode 第 859 题 "Buddy Strings"(亲密字符串)的题意、分类讨论思路与 Go 实现。题目要求判断能否通过恰好交换一次字符串 s 中的两个字符,使其与 goal 相等。读完本文,你将掌握该题的两种核心情形判定("两字符串相等"与"两字符串不等")、单次线性扫描的 O(n) 解法,以及仓库中配套单测对边界条件的覆盖方式,并可直接基于仓库源码运行验证。

题目描述

给定两个字符串sgoal,如果可以通过交换s中的两个字母得到与goal相等的结果,则返回true;否则返回false

交换字母的定义为:取两个下标ij(下标从 0 开始)且满足i != j,接着交换s[i]s[j]处的字符。

例如,在"abcd"中交换下标 0 和下标 2 的字符,可以得到"cbad"

题目给出的四个示例:

输入输出说明
s = "ab",goal = "ba"true交换s[0] = 'a's[1] = 'b'得到"ba",与 goal 相等
s = "ab",goal = "ab"false唯一可交换的是s[0]s[1],交换后得到"ba",不等于 goal
s = "aa",goal = "aa"true交换s[0] = 'a's[1] = 'a'后仍为"aa",与 goal 相等
s = "aaaaaaabc",goal = "aaaaaaacb"true交换倒数第二位与最后一位即可

题目约束:

  • 1 <= s.length, goal.length <= 2 * 10000
  • sgoal仅由小写字母组成

说明:本题的题意要点是"恰好交换一次"且必须真实执行一次交换操作,这与"两串相等即可"的直觉判断存在关键差异,也是第 2 个示例("ab"vs"ab"返回false)之所以成立的原因。

解题思路:按两种情形分类讨论

从 README 的解题思路出发,问题可以拆分为两个互斥的大分支:

情形一:s == goal,判断 s 中是否存在重复字符

当两个字符串完全相等时,要满足"交换一次后仍等于 goal",唯一可能是交换的两个字符完全相同(交换后字符串不变)。因此:

  • s中存在重复字符(至少有一个字符出现次数 ≥ 2),则可以交换该字符出现的两个位置,字符串保持不变,返回true
  • s中所有字符互不相同(如"ab"),则任何一次真实交换都会改变字符串,返回false

这就是 README 中"s等于goals中有重复元素就返回true,否则返回false"的完整含义。

情形二:s != goal,统计两串的错位位置

当两个字符串不相等时,要能通过一次交换变相等,必须满足以下全部条件:

  1. 两串长度相等(否则根本无法交换对齐);
  2. 错位位置恰好有且仅有 2 个(多于 2 个说明一次交换解决不了,少于 2 个则与s != goal矛盾);
  3. 这 2 个错位位置呈"交叉相等"关系:s[first] == goal[second]s[second] == goal[first],即交换s中这两个位置的字符后正好能对齐goal

这正是 README 中"s不等于goals中有两个下标不同的字符与goal中对应下标的字符分别相等"的展开解释。

代码实现与逐行解读

核心实现位于 859.Buddy Strings.go,完整代码如下:

package leetcode func buddyStrings(s string, goal string) bool { if len(s) != len(goal) || len(s) <= 1 { return false } mp := make(map[byte]int) if s == goal { for i := 0; i < len(s); i++ { if _, ok := mp[s[i]]; ok { return true } mp[s[i]]++ } return false } first, second := -1, -1 for i := 0; i < len(s); i++ { if s[i] != goal[i] { if first == -1 { first = i } else if second == -1 { second = i } else { return false } } } return second != -1 && s[first] == goal[second] && s[second] == goal[first] }

逐段解读如下:

1. 前置过滤(第 4~6 行)

if len(s) != len(goal) || len(s) <= 1 { return false }
  • 两串长度不等,无法通过一次交换使它们相等,直接返回false
  • 长度 ≤ 1 时,不存在"两个不同下标"可供交换(题目要求i != j),返回false。测试用例中{"a", "a"}期望结果为false正是验证这一点。

2. 情形一:s == goal(第 7~16 行)

mp := make(map[byte]int) if s == goal { for i := 0; i < len(s); i++ { if _, ok := mp[s[i]]; ok { return true } mp[s[i]]++ } return false }

利用哈希表mp记录已出现的字符:线性扫描过程中一旦发现某个字符第二次出现,说明存在可交换的重复字符对,立即返回true;扫描结束仍无重复,返回false

3. 情形二:s != goal(第 17~29 行)

first, second := -1, -1 for i := 0; i < len(s); i++ { if s[i] != goal[i] { if first == -1 { first = i } else if second == -1 { second = i } else { return false } } } return second != -1 && s[first] == goal[second] && s[second] == goal[first]
  • firstsecond记录前两个错位下标,初始为-1表示"尚未找到";
  • 单次扫描收集错位位置:找到第 3 个错位位置时,说明一次交换无法解决,立即返回false
  • 循环结束后,second != -1保证错位恰好为 2 个,再检查交叉相等关系s[first] == goal[second] && s[second] == goal[first],两者同时成立才返回true

复杂度分析

  • 时间复杂度:O(n),其中 n 为字符串长度。两个分支各自只需一次线性扫描;
  • 空间复杂度:O(1)。由于题目约束sgoal仅含小写字母,mp中最多容纳 26 个键,可视为常数级空间;若不考虑该约束,上限为 O(n)。

测试用例验证:从仓库单测看边界覆盖

仓库配套单测位于 859.Buddy Strings_test.go,除了题目给出的 4 个示例("ab"/"ba""ab"/"ab""aa"/"aa""aaaaaaabc"/"aaaaaaacb"均返回truefalse与题目一致),还补充了 3 个重要的边界用例:

输入期望输出覆盖点
{"ab", "abc"}false两串长度不等,触发前置过滤
{"a", "a"}false长度 ≤ 1,无下标对可供交换
{"abcd", "badc"}false错位位置超过 2 个(4 处),一次交换无法修复

其中{"abcd", "badc"}是对"错位多于 2 个直接返回false"分支(源码第 22~25 行)的直接验证;{"a", "a"}验证了"长度小于等于 1 直接返回false"的前置判断。所有用例均与源码行为一致,体现了仓库"100% test coverage"的测试组织方式——测试文件采用question859/para859/ans859结构体组织输入输出对,并在Test_Problem859中循环断言。

运行与验证方式

本仓库的 LeetCode 题解按题号组织在 leetcode 目录下,每个题目一个子目录,包含题号.题名.go(解法)、题号.题名_test.go(测试)与README.md(题目与思路说明)。若要在本地验证本题解法,可在仓库根目录下执行 Go 测试命令:

go test -v -run Test_Problem859 ./leetcode/0859.Buddy-Strings/

执行后将输出题目编号标识与每组输入对应的buddyStrings结果,可与 859.Buddy Strings_test.go 中的期望值逐一比对。

小结

LeetCode 859 "Buddy Strings" 是一道典型的分类讨论题,核心在于厘清"恰好交换一次"这一限定条件:

  • 两串相等时,问的是"是否存在可交换的相同字符对",即是否存在重复字符;
  • 两串不等时,问的是"错位是否恰好两处且可交叉互换对齐"。

仓库给出的 Go 解法以两次线性扫描分别覆盖这两种情形,时间复杂度 O(n)、空间复杂度 O(1),配合单测完整覆盖了长度不等、长度过短、多错位等边界情况,代码简洁且可直接复用。

【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询