题目描述:
如果可以通过将 A
中的两个小写字母精确地交换位置 K
次得到与 B
相等的字符串,我们称字符串 A
和 B
的相似度为 K
(K
为非负整数)。
给定两个字母异位词 A
和 B
,返回 A
和 B
的相似度 K
的最小值。
示例 1:
输入:A = "ab", B = "ba" 输出:1
示例 2:
输入:A = "abc", B = "bca" 输出:2
示例 3:
输入:A = "abac", B = "baca" 输出:2
示例 4:
输入:A = "aabc", B = "abca" 输出:2
提示:
1 <= A.length == B.length <= 20
A
和B
只包含集合{'a', 'b', 'c', 'd', 'e', 'f'}
中的小写字母。
题目连接:https://leetcode-cn.com/problems/k-similar-strings/description/。这是一个比较有意思的题目,也许是第一次见的缘故,甚是喜欢。参考了官方的解答,现在整理一下。
问题分析:(广度优先,BFS)
采用广度优先搜索的思想,用队列实现,首先字符串A入队queue,并记录字典 cnt(当前结点已经扩展了几次或者已经走了几步)。那么问题来了,如何获取当前节点的所有子节点那? 取出队首节点S,与字符串B比较,获取第一个不相等的字符的位置 i,那么,现在用这个位置 i 上面的字符与其后面的字符进行一次交换,使其交换后的 i 位置字符与 B 字符串 i 位置上的字符相等,每交换一次,就获得一个子节点。
列如:当前节点S = 'abcaabb', B = 'abbaabc', 所以现在 i=2,把S串中的字符 c 换成字符 b 有两种方案,所以得出该节点的所有子节点为'abbaacb'、'abbaabc'。每次入队的时,并把交换的次数(前节点的次数加 1 )记录到字典cnt中,直到结束。
Python3实现:
# @Time :2018/6/22
# @Author :LiuYinxing
# 解题思路 BFS
class Solution:
def kSimilarity(self, A, B):
def getSwap(S): # 用于获取一个节点下可以扩展的所有节点
for i, c in enumerate(S): # 找到第一个不相等的字符的-位置-
if c != B[i]: break
T = list(S)
for j in range(i + 1, len(S)): # 获取所有的可扩展的子节点
if S[j] == B[i]: # 找到就交换
T[i], T[j] = T[j], T[i]
yield ''.join(T) # 返回一个节点
T[j], T[i] = T[i], T[j] # 恢复当前环境,以便寻找下一个可扩展的节点
queue, cnt = [A], {A: 0} # 初始化队列,cnt用于记录当前结点已经走了多少步
while queue:
S = queue[0] # 出队
del queue[0]
if S == B: return cnt[S] # 结束
for T in getSwap(S): # 获取当前节点所有扩展子节点
if T not in cnt: # 如果没有出现过,则入队
cnt[T] = cnt[S] + 1
queue.append(T)
if __name__ == '__main__':
A, B = 'aabc', 'abca'
solu = Solution()
print(solu.kSimilarity(A, B))
Python3官方实现:
import collections
class Solution(object):
def kSimilarity(self, A, B):
def neighbors(S):
for i, c in enumerate(S):
if c != B[i]:
break
T = list(S)
for j in range(i+1, len(S)):
if S[j] == B[i]:
T[i], T[j] = T[j], T[i]
print("".join(T))
yield "".join(T)
T[j], T[i] = T[i], T[j]
queue = collections.deque([A])
seen = {A: 0}
while queue:
S = queue.popleft()
if S == B: return seen[S]
for T in neighbors(S):
if T not in seen:
seen[T] = seen[S] + 1
queue.append(T)
欢迎指正哦