LeetCode 859. 亲密字符串
题目描述


题意分析
必须在
s中选两个不同下标交换一次,使结果等于goal。交换不改变长度,两个下标上的字符却可以相同;所以字符串已经相等时,也要判断能否完成这次交换。
解法:模拟交换
核心思路
[!blue]
先排除长度不同的情况。若两串不相等,要改变结果就必须交换两个不同字符,这恰好改变两个位置,其他位置全部不动。因此两串必须恰好有两处差异,记为
first和second;只有一处差异无法单独修复,超过两处也不可能由一次交换解决。差异数为 2 仍然不够。交换后第一个位置放入原来的第二个字符,第二个位置放入原来的第一个字符,所以还必须满足
s[first] == goal[second]且s[second] == goal[first]。其余位置原本就相同,这两个条件成立便能保证整串相等。若两串原本相等,交换不同字符会破坏相等关系,只有交换两个相同字符才能保持结果不变。题目字符均为小写英文字母,用 26 个计数位置判断是否存在重复字符即可。没有重复字符,或者字符串只有一个字符,都无法完成要求的交换。
解题步骤
- 长度不同直接返回
false。- 若
s == goal,统计字符频次,首次发现重复字符就返回true;全部字符都不同则返回false。- 否则扫描两串,记录前两处差异。发现第三处差异时立即返回
false。- 扫描结束后,先确认第二处差异存在,再验证两处字符交叉匹配。短路判断也保证不会访问尚未赋值的下标。
代码实现
class Solution {
public boolean buddyStrings(String s, String goal) {
if (s.length() != goal.length()) {
return false;
}
// 已经相等仍须实际交换一次,只能交换重复字符
if (s.equals(goal)) {
int[] cnt = new int[26];
for (char ch : s.toCharArray()) {
cnt[ch - 'a']++;
if (cnt[ch - 'a'] > 1) {
return true;
}
}
return false;
}
int first = -1;
int second = -1;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != goal.charAt(i)) {
if (first == -1) {
first = i;
} else if (second == -1) {
second = i;
} else {
return false;
}
}
}
// 恰有两处差异时,还必须交叉匹配才能修复
return second != -1
&& s.charAt(first) == goal.charAt(second)
&& s.charAt(second) == goal.charAt(first);
}
}
func buddyStrings(s string, goal string) bool {
if len(s) != len(goal) {
return false
}
// 已经相等仍须实际交换一次,只能交换重复字符
if s == goal {
var cnt [26]int
for i := 0; i < len(s); i++ {
idx := s[i] - 'a'
cnt[idx]++
if cnt[idx] > 1 {
return true
}
}
return false
}
first := -1
second := -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]
}
复杂度分析
- 时间复杂度:$O(n)$,其中 $n$ 为字符串长度,比较和扫描均为线性过程。
- 空间复杂度:Go 辅助空间 $O(1)$;当前 Java 相等分支的字符数组转换占 $O(n)$,计数表本身为常数。
关键点总结
[!green]
- 恰好一次不同于至多一次,不能对相等输入直接成功。
- 定位两处差异后仍需验证交换能修复它们。
易错点总结
[!yellow]
- 相等时一律返回真,会错误接受没有重复字符的字符串。
- 只数两处差异而不交叉验证,会接受无法互换的字符。
- 第三处差异仅退出扫描而不失败,会忽略剩余不匹配。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 854. 相似度为 K 的字符串 | 困难 | 原题求最少任意交换次数,本题恰好只交换两个位置,还要单独考虑原串已经相同的情况。 |
| 242. 有效的字母异位词 | 简单 | 频次相同是交换可达的必要条件,但本题只允许一次交换,异位词条件本身并不足够。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!