1117 单词接龙(dfs之搜索顺序)

1. 问题描述:

单词接龙是一个与我们经常玩的成语接龙相类似的游戏。现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的"龙",每个单词最多被使用两次。在两个单词相连时,其重合部分合为一部分,例如 beast 和 astonish ,如果接成一条龙则变为 beastonish。我们可以任意选择重合部分的长度,但其长度必须大于等于1,且严格小于两个串的长度,例如 at 和 atide 间不能相连。

输入格式

输入的第一行为一个单独的整数 n 表示单词数,以下 n 行每行有一个单词(只含有大写或小写字母,长度不超过20),输入的最后一行为一个单个字符,表示"龙"开头的字母。你可以假定以此字母开头的"龙"一定存在。

输出格式

只需输出以此字母开头的最长的“龙”的长度。

数据范围

n ≤ 20

输入样例:

5
at
touch
cheat
choose
tact
a

输出样例:
23
提示
连成的"龙"为 atoucheatactactouchoose。
来源:https://www.acwing.com/problem/content/1119/

2. 思路分析:

分析题目可以知道我们需要搜索所有单词的拼接方案,在所有拼接的方案中可以拼接单词之后的最大长度,因为数据规模不大所以可以使用dfs来解决,我们需要想一个搜索顺序可以搜索出所有的方案,先预处理一下所有一个单词可以接在另外一个单词后面的情况,可以使用一个二维的g数组,因为重合的字符串长度越小那么最终拼接之后的长度越大,所以g数组可以存储两个字符串最小需要重合的长度,可以使用三层循环暴力枚举,预处理出任意两个单词是否存在边,边权为两个字符串重合的最小长度,这样dfs搜索的时候可以直接判断是否有边就可以判断是否可以接在其后面,如果可以拼接那么此时拼接在后面得到的长度是更大的,因为可能存在多个起点所以我们使用循环找出所有以start开头的单词然后暴力枚举所有的拼接情况,使用一个used数组来记录每一个单词的使用次数,当使用次数小于2的时候才往下递归,为了保证在往下递归的时候与一开始递归的状态是一致的所以在回溯的时候要恢复现场,恢复调用之前的状态。

3. 代码如下:

from typing import List


class Solution:
    res = None

    def dfs(self, dragon: str, last: int, n: int, used: List[int], g: List[List[int]], words: List[str]):
        used[last] += 1
        # 更新拼接的最大长度
        self.res = max(self.res, len(dragon))
        for i in range(n):
            # 因为每个单词最多使用两次所以在往下递归的时候需要先判断一下
            if g[last][i] > 0 and used[i] < 2:
                k = g[last][i]
                # 注意重合的部分只取其中一个字符串中重复的部分
                self.dfs(dragon + words[i][k:], i, n, used, g, words)
        # 回溯, 恢复到调用之前的状态
        used[last] -= 1

    def process(self):
        n = int(input())
        words = list()
        for i in range(n):
            words.append(input())
        # 龙开始的字符
        start = input()
        # 最多有21个单词, g[i][j]表示第i个单词与第j个单词的最小的重合长度
        g = [[0] * 21 for i in range(21)]
        for i in range(n):
            for j in range(n):
                # 暴力枚举长度
                sa, sb = words[i], words[j]
                # 从小到大枚举字符串sa后缀的长度
                for k in range(1, min(len(sa), len(sb))):
                    if sa[len(sa) - k:] == sb[0:k]:
                        # 因为重合的长度越小那么龙的长度更长, 所以找到之后直接break
                        g[i][j] = k
                        break
        self.res = 0
        used = [0] * 21
        for i in range(len(words)):
            # 从每一个可以开始的单词开始搜索
            if words[i][0] == start:
                self.dfs(words[i], i, n, used, g, words)
        return self.res


if __name__ == '__main__':
    print(Solution().process())

おすすめ

転載: blog.csdn.net/qq_39445165/article/details/121730842