NC16758 [NOIP2000]单词接龙

题目链接

题目

题目描述

单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如beast和astonish,如果接成一条龙则变为beastonish,另外相邻的两部分不能存在包含关系,例如at和atide间不能相连。

输入描述

输入的第一行为一个单独的整数n(n ≤ 20)表示单词数,以下n行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在.

输出描述

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

示例1

输入

5
at
touch
cheat
choose
tact
a

输出

23

说明

连成的“龙”为 atoucheatactactouchoose

题解

知识点:DFS。

先要预处理出单词之间互相能衔接的最短长度方便搜索,\(link[i][j]\) 表示第 \(i\) 个单词和第 \(j\) 个单词能衔接的最短长度,如果不能衔接则为 \(-1\) ,初始时用 \(0\) 表示可开始的单词;用 \(vis[i]\) 表示第 \(i\) 个单词用到几次,两次就不能再用。然后一个简单的搜索,每次更新最大值。

时间复杂度 \(O(?)\)

空间复杂度 \(O(n^2)\)

代码

#include <bits/stdc++.h>

using namespace std;

int n;
string words[27];
int link[27][27];///x,y单词的衔接长度
int vis[27];
int ans;

int check(int x, int y) {
    for (int i = 1;i < min(words[x].size(), words[y].size());i++) {
        bool ok = true;
        for (int j = words[x].size() - i;j < words[x].size();j++)
            ok &= words[x][j] == words[y][j - (words[x].size() - i)];
        if (ok) return i;
    }
    return -1;
}

void dfs(int pos = 0, int sum = 0) {
    ans = max(ans, sum);
    for (int i = 1;i <= n;i++) {
        if (vis[i] == 2 || !~link[pos][i]) continue;
        vis[i]++;
        dfs(i, sum + words[i].size() - link[pos][i]);
        vis[i]--;
    }
}


int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n;
    for (int i = 1;i <= n;i++) cin >> words[i];
    cin >> words[0];
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= n;j++)
            link[i][j] = check(i, j);
    for (int i = 1;i <= n;i++) link[0][i] = words[0][0] == words[i][0] ? 0 : -1;
    dfs();
    cout << ans << '\n';
    return 0;
}
posted @ 2022-07-16 16:13  空白菌  阅读(36)  评论(0编辑  收藏  举报