在给定列表中搜索将字符串转换为另一个字符串的最短方法,一次一个字符

Posted

技术标签:

【中文标题】在给定列表中搜索将字符串转换为另一个字符串的最短方法,一次一个字符【英文标题】:Search for the shortest way to turn a string to another string one character at a time in a given list 【发布时间】:2021-03-19 23:58:46 【问题描述】:

给定一个具有 SAME 长度的字符串list,寻找一种方法将start 字符串转换为end 字符串,一次一个字符,这样每个转换后的字符串都是仍然存在于字符串的list 中。

输入! 输入以T 开头表示测试用例的数量。然后,在另一行中出现m 询问要放入列表的字符串数。 m 后面的行要求相同长度的字符串,然后,最后一行由startend 组成,以空格分隔。

例子:

List: ["booster", "rooster", "roaster", "coaster", "coasted"] 
start: roaster
end: booster

Number of String Transformation: 3 (roaster -> rooster -> booster)


List: ["booster", "rooster", "roaster", "coaster", "coasted"] 
start: booster
end: coasted

Number of String Transformation: 3 (booster -> rooster -> roaster -> coaster -> coasted)


List: ["booster", "rooster", "roaster", "coaster", "coasted", "coastal"] 
start: roaster
end: coastal

Number of String Transformation: impossible (roaster -> coaster -> coasted -> x -> coastal)

如果没有可能,输出impossible。否则,输出最短方式进行变换。

我采用了一种递归方法,但这无法找到所有可能的解决方案,因为它只尝试更改与 end 字符串不相等的索引,这会跳过 list 中存在的一些字符串,这可能导致解决方案。

我的代码适用于第一个示例,但错过了第二个示例,因为它没有尝试 (booster -> rooster -> etc...),因为它只将 (booster -> cooster -> coaster -> coasted) 视为可能的解决方案。我有点卡住了,我不知道如何解决这个问题。有人可以告诉我更好的方法吗?

def transformable(word_1, word_2):
    return len([i for i in range(len(word_1)) if word_1[i] != word_2[i]]) == 1

def shortest_transformation(start, end, words):
    queue = [[start]]
    available = set(words).difference(start)
    while queue:
        path = queue.pop()
        if transformable(path[-1], end):
            return path + [end]
        transformables = set(word for word in available if transformable(path[-1], word))
        queue = [path + [word] for word in transformables] + queue
        available = available.difference(transformables)
    return None

T = int(input())

for case in range(T):
    m = int(input())
    words = []
    for inputs in range(m):
        words.append(input())
    
    start, end = input().split()
    
    result = shortest_transformation(start, end, words)
    print(len(result)) if result else print("none")

4
5
booster
rooster
roaster
coaster
coastal
booster coastal
6
booster
rooster
roaster
coaster
coasted
coastal
booster coastal
5
booster
rooster
roaster
coaster
coasted
booster coasted
5
booster
rooster
roaster
coaster
coasted
roaster booster

Output:
none
none
5
3

【问题讨论】:

1) 你听说过最短路径问题吗? 2)你能找到最短路径问题和你的问题之间的关系吗? 3)你知道解决最短路径问题的算法吗? 4) 你能从这个算法中启发自己写一个算法来解决你的问题吗? 究竟是哪一个?在我们班上,我们还在讨论分而治之的算法,你说的是这个吗? 不,我说的是最短路径问题:在图中找到两个顶点之间的最短路径。 我不完全确定 T 到底是做什么的,但你不应该将 result …print(… 行缩进到 case 的循环中吗? 没关系,我真的认为你的代码应该可以工作。唯一的问题是我们的编译器如何处理输入。无论如何,我真的很感谢你的帮助。感谢您的尝试! 【参考方案1】:

如果您还没有找到解决方案,这里有一个建议:

from collections import deque

def transformable(word_1, word_2):
    return len([i for i in range(len(word_1)) if word_1[i] != word_2[i]]) == 1

def shortest_transformation(start, end, words):
    queue = deque([[start]])
    words = set(words).difference(start)
    while queue:
        path = queue.pop()
        if transformable(path[-1], end):
            return path + [end]
        transformables = set(word for word in words if transformable(path[-1], word))
        queue.extendleft(path + [word] for word in transformables)
        words = words.difference(transformables)
    return []

这是一种广度优先搜索(通过使用deque.extendleft() 方法的LILO/FIFO 队列),这是搜索最短路径的好方法。

你的例子

words = ["booster", "rooster", "roaster", "coaster", "coasted"] 
start = 'roaster'
end = 'booster'
print(shortest_transformation(start, end, words))

words = ["booster", "rooster", "roaster", "coaster", "coasted"] 
start = 'booster'
end = 'coasted'
print(shortest_transformation(start, end, words))

words = ["booster", "rooster", "roaster", "coaster", "coasted", "coastal"] 
start = 'roaster'
end = 'coastal'
print(shortest_transformation(start, end, words))

输出是

['roaster', 'rooster', 'booster']
['booster', 'rooster', 'roaster', 'coaster', 'coasted']
[]

如果您不想使用deque,那么这应该是等效的:

def shortest_transformation(start, end, words):
    queue = [[start]]
    available = set(words).difference(start)
    while queue:
        path = queue.pop()
        if transformable(path[-1], end):
            return path + [end]
        transformables = set(word for word in available if transformable(path[-1], word))
        queue = [path + [word] for word in transformables] + queue
        available = available.difference(transformables)
    return None

【讨论】:

我尝试了这段代码,但我遇到了错误。 @muw 你能详细说明一下:什么样的错误? 好吧,如果我只是运行它,它可以与预期的输出一起工作,但是当我尝试逐行调试以了解您的方法时,它给了我这些错误: Traceback (last recent call last): File " I:\Program Files\Thonny\lib\site-packages\thonny\workbench.py​​”,第 1598 行,在 event_generate handler(event) 文件“I:\Program Files\Thonny\lib\site-packages\thonny\plugins\ debugger.py”,第 1186 行,在 _handle_debugger_progress _current_debugger.handle_debugger_progress(msg) 比这长很多,有很多错误,我无法确定原因。 @muw 在调试模式下运行良好(PyCharm,在 macOS 和 Windows10 上)?我认为这是您的环境的限制,我不熟悉 - 不是编码错误。我添加了一个不使用dequeue 的版本,只有一个列表。也许有帮助。 再次尝试了你的代码,它适用于我创建的测试用例,但对于我们的类编译器,它有三个隐藏的测试用例,它只通过了一个,而它给了 2 个IndexError其他隐藏案例。已经一天了,我仍然无法找出您的代码中的漏洞。您的代码是否考虑了多条路径并且只输出最短路径?

以上是关于在给定列表中搜索将字符串转换为另一个字符串的最短方法,一次一个字符的主要内容,如果未能解决你的问题,请参考以下文章

最短回文串

如何将数组转换为可能的最短字符串

给定 2 个字符串和整数 k,以 k 步将一个字符串转换为另一个字符串

在java中初始化字符串列表的最短方法是啥?

最短编辑距离

在巨大列表中查找/搜索的最有效方法(python)