Python,Recover a secret string from random triplets

# # 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)
posted @ 2025-03-25 15:16  Kazuma_124  阅读(33)  评论(0)    收藏  举报