LeetCode 854. K-Similar Strings
问题描述:
Strings A
and B
are K
-similar (for some non-negative integer K
) if we can swap the positions of two letters in A
exactly K
times so that the resulting string equals B
.
Given two anagrams A
and B
, return the smallest K
for which A
and B
are K
-similar.
输入输出实例:
Example 1:
Input: A = "ab", B = "ba"
Output: 1
Example 2:
Input: A = "abc", B = "bca"
Output: 2
Example 3:
Input: A = "abac", B = "baca"
Output: 2
Example 4:
Input: A = "aabc", B = "abca"
Output: 2
Note:
1 <= A.length == B.length <= 20
A
andB
contain only lowercase letters from the set{'a', 'b', 'c', 'd', 'e', 'f'}
题解:
Ok,一上来我们当然可以想到暴力DFS
一下啦,走一步是一步,总能走到的是吧,但是仔细想想,这样会产生一些无用的状态呀,例如交换了一些没有必要交换的位置,这样肯定是相当耗时的。嗯,仔细审审题,要最少的交换来完成,最少,最短,没错——就是BFS
了。
算法思想为:首先拿到初始节点A
,寻找其子节点,作为BFS
搜索树的第二层,接着第二层的节点的子节点作为第三层…
那么怎么寻找其子节点呢,我们可以通过从左往右扫描当前节点字符串和目标节点B
,找到第一个不同字母的位置,然后接着扫描该位置后面的字母,找到满足:
current_node[i] == B[p],p为上面找到的不同字母的位置
current_node[i] != B[p]
的坐标i
,swap
后先判断是否已经访问过该状态(通过set<string> visited
),没访问过才压入队列作为子节点。(为什么要满足第二个条件呢,因为已经相同字母的位置就没有必要去交换了不是吗?)
举个栗子:
当前节点为abcbba
,目标节点B
为abbbac
,那么我们可以找到这么两个子节点:abbcba
和abbbca
。
Code:
class Solution {
public:
int kSimilarity(string A, string B) {
queue<string> q;
set<string> visited;
int count = 0;
q.push(A);
visited.insert(A);
while (!q.empty()) {
// then we don't need two queue for different level nodes
int cur_level_que_size = q.size();
for (int i = 0; i < cur_level_que_size; ++i) {
string s = q.front();
q.pop();
// current pos of inequal letter
int p = 0;
while (s[p] == B[p] && p < s.length()) ++p;
if (p == s.length()) return count;
for (int j = p + 1; j < s.length(); ++j) {
if (s[j] == B[j] || s[j] != B[p]) continue;
if (s[j] == B[p]) {
string tmp = swap(s, j, p);
if (!visited.count(tmp)) {
q.push(tmp);
visited.insert(tmp);
}
}
}
}
++count;
}
return count;
}
private:
string swap(string s, int x, int y) {
char c = s[x];
s[x] = s[y];
s[y] = c;
return s;
}
};
复杂度:
这个算法的复杂度实在不那么好分析,只能说相比DFS
,其搜索会更有效率,因为我们在探索每层子节点时,会排除不少的状态,虽说DFS
也能用相同的策略,但从直觉上来说,BFS
确实是看起来更有效率的方式。
菜哭,逃 ?