日报
- 今日并查集以为是水题结果wa了一次。
题目
一、 面试题 17.07. 婴儿名字
链接: 面试题 17.07. 婴儿名字
1. 题目描述
2. 思路分析
- 题面很清晰,其实就是并查集归出所有的祖宗节点(同族字典序最小的单词),然后每个数的频率都统计到祖宗头上。
- 我wa了一次在于union之前判断x,y的字典序,然后把x合并向y,这样是错误的,因为路径压缩后,x的祖宗和y的祖宗字典序不一定谁大谁小呢,因此要在union内部:x,y都路径压缩后比较祖宗的字典序,然后选择方向合并。
- 由于我的并查集模板是数组的,因此需要先对单词进行离散化,映射成数字下标进行并查集,比较麻烦。下边打算整理一个基于字典的模板。
- 实测不离散化更快。
- 基于字典的并查集要注意,findfather和union之前,把x,y都setdefault本身
3. 代码实现
原答案
class UnionFind:
def __init__(self,size):
self.fathers = list(range(size))
def find_father(self,x):
return self._zip_find_father(x)
def _zip_find_father(self,x):
fathers = self.fathers
if fathers[x] != x:
fathers[x] = self._zip_find_father(fathers[x])
return fathers[x]
def union(self,x,y):
x = self.find_father(x)
y = self.find_father(y)
if x == y:
return False
if x<y:
x,y=y,x
self.fathers[x] = y
return True
def is_same_father(self,x,y):
return self.find_father(x)==self.find_father(y)
class Solution:
def trulyMostPopular(self, names: List[str], synonyms: List[str]) -> List[str]:
# print(names[0].split('('))
frq = {}
for name in names:
a = name.split('(')
b = a[1].split(')')
frq[a[0]]=int(b[0])
hashed = sorted(frq.keys())
n = len(hashed)
ans = defaultdict(int)
uf = UnionFind(n)
for s in synonyms:
ss = s.split(',')
a = ss[0].split('(')[1]
b = ss[1].split(')')[0]
x = bisect_left(hashed,a)
y = bisect_left(hashed,b)
if x<n and y <n:
uf.union(x,y)
for a,b in frq.items():
f = uf.find_father(bisect_left(hashed,a))
ans[f] += b
return [f'{hashed[a]}({b})' for a,b in ans.items()]
基于字典的并查集
class UnionFind:
def __init__(self,eles):
self.fathers = {}
f = self.fathers
for e in eles:
f[e] = e
def find_father(self,x):
f = self.fathers
f.setdefault(x,x)
return self._zip_find_father(x)
def _zip_find_father(self,x):
fathers = self.fathers
if fathers[x] != x:
fathers[x] = self._zip_find_father(fathers[x])
return fathers[x]
def union(self,x,y):
f = self.fathers
f.setdefault(x,x)
f.setdefault(y,y)
x = self.find_father(x)
y = self.find_father(y)
if x == y:
return False
if x<y:
x,y=y,x
self.fathers[x] = y
return True
def is_same_father(self,x,y):
return self.find_father(x)==self.find_father(y)
class Solution:
def trulyMostPopular(self, names: List[str], synonyms: List[str]) -> List[str]:
# 频率入字典
frq = {}
for name in names:
a = name.split('(')
b = a[1].split(')')
frq[a[0]]=int(b[0])
# 构建并查集
uf = UnionFind(frq.keys())
for s in synonyms:
a,b = s[1:-1].split(',')
uf.union(a,b)
# 构建答案
ans = defaultdict(int)
for a,b in frq.items():
f = uf.find_father(a)
ans[f] += b
return [f'{a}({b})' for a,b in ans.items()]