# # Recover a secret string from random triplets
# # https://www.codewars.com/kata/53f40dff5f9d31b813000774/train/python
#我的解法(室友教的)
# Completed in 1.34ms
#使用拓扑排序算法,拓扑排序算法是一种对有向无环图进行排序的算法
#拓扑排序算法的步骤如下:
#1.从有向图中选择一个没有前驱的顶点并输出
#2.从有向图中删除该顶点和所有以它为起点的边
#3.重复1和2直到所有顶点都被输出
#如果有向图中有环,则无法进行拓扑排序
from collections import defaultdict
import queue
def recover_secret(triplets):
in_degree = defaultdict(int)
end_map = defaultdict(list)
for [a,b,c] in triplets:
in_degree[a]+=0
in_degree[b]+=1
in_degree[c]+=1
end_map[a].append(b)
end_map[b].append(c)
zero_in_degree = queue.Queue()
for key in in_degree.keys():
if in_degree[key]==0:
zero_in_degree.put(key)
res = ''
while(zero_in_degree.qsize()):
char = zero_in_degree.get()
res+=char
for end in end_map[char]:
in_degree[end]-=1
if in_degree[end]==0:
zero_in_degree.put(end)
if(len(res)!=len(in_degree)):
return 'error'
return res
triplets = [
['t', 'u', 'p'],
['w', 'h', 'i'],
['t', 's', 'u'],
['a', 't', 's'],
['h', 'a', 'p'],
['t', 'i', 's']
]
print(recover_secret(triplets))
# 别人的解法 1
# Completed in 2.71ms
def recover_secret(triplets):
# 将所有的字母提取出来,存放到letters中
# 不停地遍历triplets,每遍历一个triplet,保证letters中这三个字母的相对顺序是正确的
# 直到一次遍历triplets后,letters中的顺序没有发生变化,则说明letters中的字母顺序已符合triplets
letters = list(set([l for triplet in triplets for l in triplet]))
# letters = list(set([l for l in triplet for triplet in triplets ])) #这样不行,会显示triplet未定义,说明是从左往右执行的
isChanged = True
while isChanged:
isChanged = False
for [a,b,c] in triplets:
indexs = [letters.index(a),letters.index(b),letters.index(c)]
if not (indexs[0]<indexs[1] and indexs[1]<indexs[2]):
indexs.sort()
letters[indexs[0]],letters[indexs[1]],letters[indexs[2]] = a,b,c
isChanged = True
return ''.join(letters)
# 别人的解法 2
# Completed in 7.55ms
def recoverSecret(triplets):
#letters保存所有字符
letters = set(x for triplet in triplets for x in triplet)
# map each character to a value in the final ordering
# initially, all characters are at 0
positions = {char:0 for char in letters}
# map each letter to a tuple of sets, one for all
# letters to the left and one for all to the right of it
# partition 分割,隔墙
partitions = {char:(set(),set()) for char in letters}
for tri in triplets:
a,b,c = tri
# add b,c to right of a
partitions[a][1].add(b)
partitions[a][1].add(c)
# add a,c to left/right of b
partitions[b][0].add(a)
partitions[b][1].add(c)
# add a,b to left of c
partitions[c][0].add(a)
partitions[c][0].add(b)
# recursively(递归地) decrement(减少) a character's position,
# and the position of all characters to its left
def move_left(char, seen):
# 避免重复减少
if char in seen:
return
positions[char] -= 1
seen.add(char)
for l_char in partitions[char][0]:
move_left(l_char, seen)
# recursively increment a character's position,
# and the position of all characters to its right
def move_right(char, seen):
if char in seen:
return
positions[char] += 1
seen.add(char)
for r in partitions[char][1]:
move_right(r, seen)
# Perform the infamous "STAND ASIDE" algorithm:
# Every character pushes its left/right neighbours
# away from itself, with VEHEMENCE and PREJUDICE.
# There's some repetition here, but we can afford to
# be lazy because the author was lazy and didn't implement
# random tests. We could have hard-coded this stuff, but
# the STAND-ASIDE algorithm is more glorious.
for left, right in partitions.values():
for l in left:
move_left(l, set())
for r in right:
move_right(r, set())
# sort by position and concatenate the result
return "".join(sorted(positions, key=lambda x: positions[x]))
triplets = [
['t', 'u', 'p'],
['w', 'h', 'i'],
['t', 's', 'u'],
['a', 't', 's'],
['h', 'a', 'p'],
['t', 'i', 's']
]
print(recoverSecret(triplets))
# 失败的尝试
# collections是python标准库中的一个模块,提供了几种额外的数据结构
# defaultdict是collections模块中的一个类,它是dict的子类,提供了一个工厂函数,为字典提供了默认值
# defaultdict在访问一个不存在的key时,不会抛出KeyError,而是自动为这个key创建一个默认值
# from collections import defaultdict
# def recover_secret(triplets):
# values_map = defaultdict(int)
# for [a,b,c] in triplets:
# values_map[a] += 1
# values_map[b] += 2
# values_map[c] += 3
# sorted_chars = sorted(values_map.keys(), key=lambda x: values_map[x])
# return ''.join(sorted_chars)
# triplets = [
# ['t', 'u', 'p'],
# ['w', 'h', 'i'],
# ['t', 's', 'u'],
# ['a', 't', 's'],
# ['h', 'a', 'p'],
# ['t', 'i', 's']
# ]
# # t<u<p
# # [t<u<p w<h<i]
# # [t<[u<p s] w<h<i]
# # [t<s<u<p w<h<i]
# # [a<t<s<u<p w<h<i]
# # w<h<[a<t<s<u<p i]
# # w<h<a<t<[s<u<p i]
# # w<h<a<t<i<s<u<p
# print(recover_secret(triplets))
# from collections import defaultdict
# def recover_secret(triplets):
# weights_map = defaultdict(set)
# def upgrade(a,b):
# for ch in weights_map[a]:
# weights_map[b]|=weights_map[ch]
# for [a,b,c] in triplets:
# weights_map[a]|={a}
# weights_map[b]|={b}
# upgrade(a,b)
# weights_map[c]|={c}
# upgrade(b,c)
# # enumerate() 允许在遍历课迭代对象时,同时获取元素的索引盒值,
# # 该函数返回包含索引盒值的元组
# # for i, c in enumerate(triplet):
# # weights_map[c] += 3**i
# # sorted() 返回一个排序后的列表
# sorted_chars = sorted(weights_map.keys(), key=lambda x: len(weights_map[x]))
# return ''.join(sorted_chars)