题目
标题和出处
标题:亲密字符串
出处:859. 亲密字符串
难度
3 级
题目描述
要求
给你两个字符串 s \texttt{s} s 和 goal \texttt{goal} goal,只要我们可以通过交换 s \texttt{s} s 中的两个字母得到与 goal \texttt{goal} goal 相等的结果,就返回 true \texttt{true} true;否则返回 false \texttt{false} false。
交换字母的定义是:取两个下标 i \texttt{i} i 和 j \texttt{j} j(下标从 0 \texttt{0} 0 开始)且满足 i ≠ j \texttt{i} \ne \texttt{j} i=j,并交换 s[i] \texttt{s[i]} s[i] 和 s[j] \texttt{s[j]} s[j] 处的字符。
- 例如,在 "abcd" \texttt{"abcd"} "abcd" 中交换下标 0 \texttt{0} 0 和下标 2 \texttt{2} 2 的元素可以生成 "cbad" \texttt{"cbad"} "cbad"。
示例
示例 1:
输入:
s
=
"ab",
goal
=
"ba"
\texttt{s = "ab", goal = "ba"}
s = "ab", goal = "ba"
输出:
true
\texttt{true}
true
解释:你可以交换
s[0]
=
‘a’
\texttt{s[0] = `a'}
s[0] = ‘a’ 和
s[1]
=
‘b’
\texttt{s[1] = `b'}
s[1] = ‘b’ 生成
"ba"
\texttt{"ba"}
"ba",此时
s
\texttt{s}
s 和
goal
\texttt{goal}
goal 相等。
示例 2:
输入:
s
=
"ab",
goal
=
"ab"
\texttt{s = "ab", goal = "ab"}
s = "ab", goal = "ab"
输出:
false
\texttt{false}
false
解释:你只能交换
s[0]
=
‘a’
\texttt{s[0] = `a'}
s[0] = ‘a’ 和
s[1]
=
‘b’
\texttt{s[1] = `b'}
s[1] = ‘b’ 生成
"ba"
\texttt{"ba"}
"ba",此时
s
\texttt{s}
s 和
goal
\texttt{goal}
goal 不相等。
示例 3:
输入:
s
=
"aa",
goal
=
"aa"
\texttt{s = "aa", goal = "aa"}
s = "aa", goal = "aa"
输出:
true
\texttt{true}
true
解释:你可以交换
s[0]
=
‘a’
\texttt{s[0] = `a'}
s[0] = ‘a’ 和
s[1]
=
‘a’
\texttt{s[1] = `a'}
s[1] = ‘a’ 生成
"aa"
\texttt{"aa"}
"aa",此时
s
\texttt{s}
s 和
goal
\texttt{goal}
goal 相等。
示例 4:
输入:
s
=
"aaaaaaabc",
goal
=
"aaaaaaacb"
\texttt{s = "aaaaaaabc", goal = "aaaaaaacb"}
s = "aaaaaaabc", goal = "aaaaaaacb"
输出:
true
\texttt{true}
true
数据范围
- 1 ≤ s.length, goal.length ≤ 2 × 10 4 \texttt{1} \le \texttt{s.length, goal.length} \le \texttt{2} \times \texttt{10}^\texttt{4} 1≤s.length, goal.length≤2×104
- s \texttt{s} s 和 goal \texttt{goal} goal 由小写英语字母组成
解法
思路和算法
由于交换字符串中的两个字母不会改变字符串的长度,因此如果可以通过交换 s s s 中的两个字母得到 goal \textit{goal} goal,则一定有 s s s 和 goal \textit{goal} goal 的长度相同。如果 s s s 和 goal \textit{goal} goal 的长度不同,则返回 false \text{false} false。
当 s s s 和 goal \textit{goal} goal 的长度相同时,如果可以通过交换 s s s 中的两个字母得到 goal \textit{goal} goal,则交换的两个字母可能相同或不同。
-
如果交换的两个字母相同,则 s s s 和 goal \textit{goal} goal 相等且至少有一个字母在同一个字符串中出现超过 1 1 1 次。
-
如果交换的两个字母不同,则 s s s 和 goal \textit{goal} goal 有两个下标处的字母不同,其余字母相同,假设字母不同的两个下标是 index 1 \textit{index}_1 index1 和 index 2 \textit{index}_2 index2, index 1 ≠ index 2 \textit{index}_1 \ne \textit{index}_2 index1=index2,则有 s [ index 1 ] = goal [ index 2 ] s[\textit{index}_1] = \textit{goal}[\textit{index}_2] s[index1]=goal[index2] 且 s [ index 2 ] = goal [ index 1 ] s[\textit{index}_2] = \textit{goal}[\textit{index}_1] s[index2]=goal[index1]。
根据上述分析,可以同时遍历 s s s 和 goal \textit{goal} goal,判断是否可以通过交换 s s s 中的两个字母得到 goal \textit{goal} goal。遍历过程中需要记录每个字母的出现次数、是否有重复字母、字母不同的下标个数和字母不同的下标。对于每个下标 i i i,进行如下操作:
-
将 s [ i ] s[i] s[i] 的出现次数加 1 1 1,如果该字母的出现次数大于 1 1 1,则有重复字母;
-
如果 s [ i ] ≠ goal [ i ] s[i] \ne \textit{goal}[i] s[i]=goal[i],则将字母不同的下标个数加 1 1 1,并更新字母不同的下标,如果字母不同的下标个数大于 2 2 2,则返回 false \text{false} false。
遍历结束之后,字母不同的下标个数一定不会大于 2 2 2(否则在遍历过程中就返回 false \text{false} false)。根据字母不同的下标个数,结果分别如下:
-
如果字母不同的下标个数是 1 1 1,则返回 false \text{false} false;
-
如果字母不同的下标个数是 0 0 0,则当有重复字母时返回 true \text{true} true,否则返回 false \text{false} false;
-
如果字母不同的下标个数是 2 2 2,则当 s [ index 1 ] = goal [ index 2 ] s[\textit{index}_1] = \textit{goal}[\textit{index}_2] s[index1]=goal[index2] 和 s [ index 2 ] = goal [ index 1 ] s[\textit{index}_2] = \textit{goal}[\textit{index}_1] s[index2]=goal[index1] 都成立时返回 true \text{true} true,否则返回 false \text{false} false。
代码
class Solution {
public boolean buddyStrings(String s, String goal) {
if (s.length() != goal.length()) {
return false;
}
int[] counts = new int[26];
boolean repeat = false;
int length = s.length();
int different = 0, index1 = -1, index2 = -1;
for (int i = 0; i < length; i++) {
char c1 = s.charAt(i), c2 = goal.charAt(i);
counts[c1 - 'a']++;
if (counts[c1 - 'a'] > 1) {
repeat = true;
}
if (c1 != c2) {
different++;
if (different == 1) {
index1 = i;
} else if (different == 2) {
index2 = i;
} else {
return false;
}
}
}
if (different == 1) {
return false;
}
if (different == 0) {
return repeat;
} else {
return s.charAt(index1) == goal.charAt(index2) && s.charAt(index2) == goal.charAt(index1);
}
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 和 goal \textit{goal} goal 的长度。需要遍历两个字符串各一次,遍历过程中维护相关信息,遍历结束之后判断的时间是 O ( 1 ) O(1) O(1)。
-
空间复杂度: O ( ∣ Σ ∣ ) O(|\Sigma|) O(∣Σ∣),其中 Σ \Sigma Σ 是字符集,这道题中 Σ \Sigma Σ 是全部小写英语字母, ∣ Σ ∣ = 26 |\Sigma| = 26 ∣Σ∣=26。空间复杂度主要取决于哈希表,需要使用哈希表记录每个字母的出现次数。