LeetCode 839题"相似字符串组"是一个经典的并查集应用问题。题目给定一个字符串列表,要求我们找出其中互为"相似"字符串的组数。这里的"相似"定义为:两个字符串可以通过交换两个字符的位置变得相同(注意:交换次数仅限一次)。
这个问题在实际开发中有诸多应用场景,比如:
首先我们需要明确如何判断两个字符串是否相似。根据题意:
这个问题本质上是求图的连通分量个数,每个字符串是图中的一个节点,相似关系构成边。并查集(Union-Find)是解决这类问题的理想数据结构,因为它可以高效地:
并查集的典型实现包括路径压缩和按秩合并两种优化,可以将操作的时间复杂度降至接近常数级别。
python复制class UnionFind:
def __init__(self, size):
self.parent = list(range(size))
self.rank = [0] * size
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # 路径压缩
return self.parent[x]
def union(self, x, y):
x_root = self.find(x)
y_root = self.find(y)
if x_root == y_root:
return
# 按秩合并
if self.rank[x_root] < self.rank[y_root]:
self.parent[x_root] = y_root
else:
self.parent[y_root] = x_root
if self.rank[x_root] == self.rank[y_root]:
self.rank[x_root] += 1
python复制def is_similar(a, b):
if len(a) != len(b):
return False
diff = []
for i in range(len(a)):
if a[i] != b[i]:
diff.append(i)
if len(diff) > 2:
return False
return len(diff) == 0 or (len(diff) == 2 and a[diff[0]] == b[diff[1]] and a[diff[1]] == b[diff[0]])
python复制def numSimilarGroups(strs):
n = len(strs)
uf = UnionFind(n)
for i in range(n):
for j in range(i+1, n):
if is_similar(strs[i], strs[j]):
uf.union(i, j)
# 统计连通分量个数
groups = set()
for i in range(n):
groups.add(uf.find(i))
return len(groups)
这个问题可以扩展应用到多个实际场景:
在实际工程实现中,可能需要考虑:
在实际编码过程中,有几点特别值得注意:
并查集的路径压缩和按秩合并虽然看似简单,但对性能影响巨大。我曾经在一个1000字符串的测试用例上,未优化的版本超时,而优化后的版本仅需几百毫秒。
相似性判断函数的边界条件很容易遗漏。建议先写测试用例再实现函数,特别是要测试:
在LeetCode上提交时,Python的默认递归深度可能不够,可以考虑改用迭代实现的find函数:
python复制def find(self, x):
while self.parent[x] != x:
self.parent[x] = self.parent[self.parent[x]] # 路径压缩
x = self.parent[x]
return x