【ACWing】1117. 单词接龙

题目地址:

https://www.acwing.com/problem/content/1119/

单词接龙是一个与我们经常玩的成语接龙相类似的游戏。现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”,每个单词最多被使用两次。在两个单词相连时,其重合部分合为一部分,例如beastastonish,如果接成一条龙则变为beastonish

我们可以任意选择重合部分的长度,但其长度必须大于等于1,且严格小于两个串的长度,例如atatide间不能相连。

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

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

数据范围:
n ≤ 20 n≤20 n20

两个单词 s s s t t t,如果 t t t能接在 s s s的后面,那么一定是 t t t的某个后缀等于 t t t的某个前缀,并且这个相等的部分的长度不能等于 min ⁡ { l s , l t } \min\{l_s,l_t\} min{ ls,lt}。为了使得接龙的长度更加长,我们肯定要找到最短的那个前后缀(例如s = "abcbc",而t = "bcbcd",那么我们肯定要两个拼接为"abcbcbcd"而不是"abcbcd",前者只利用了重合部分"bc"而后者用了"bcbc")。我们可以先预处理一下每两个字符串满足上述条件的最短前后缀长度,然后开始枚举以”龙“开始的字符串,接着DFS暴搜。代码如下:

#include <iostream>
using namespace std;

const int N = 21;
int n;
string word[N];
// g[i][j]存word[i]和word[j]相等的最短前后缀长度(word[i]的后缀和word[j]的前缀),
// 如果不存在则赋值为0
int g[N][N];
// 存word[i]被用了多少次
int used[N];
// 存全局答案
int res;

// s是当前接龙的长度,cur存当前最新接到龙后面的单词下标
void dfs(string s, int cur) {
    
    
    res = max((int) s.size(), res);
    used[cur]++;

    for (int i = 0; i < n; i++)
        if (g[cur][i] && used[i] < 2)
            dfs(s + word[i].substr(g[cur][i]), i);

	// 回溯恢复现场
    used[cur]--;
}

int main() {
    
    
    cin >> n;
    for (int i = 0; i < n; i++) cin >> word[i];

    char st;
    cin >> st;

    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) {
    
    
            string a = word[i], b = word[j];
            for (int k = 1; k < min(a.size(), b.size()); k++)
                if (a.substr(a.size() - k) == b.substr(0, k)) {
    
    
                    g[i][j] = k;
                    break;
                }
        }

    for (int i = 0; i < n; i++)
        if (word[i][0] == st)
            dfs(word[i], i);

    cout << res << endl;

    return 0;
}

时间复杂度与具体是怎样的输入有关,可以认为是指数级(或以上),空间复杂度 O ( n 2 ) O(n^2) O(n2)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/114815615