[luogu p1127] 词链

传送门

词链

题目描述

如果单词 \(X\) 的末字母与单词 \(Y\) 的首字母相同,则 \(X\)\(Y\) 可以相连成 \(X.Y\)。(注意:\(X\)\(Y\) 之间是英文的句号 .)。例如,单词 dog 与单词 gopher,则 doggopher 可以相连成 dog.gopher

另外还有一些例子:

  • dog.gopher
  • gopher.rat
  • rat.tiger
  • aloha.aloha
  • arachnid.dog

连接成的词可以与其他单词相连,组成更长的词链,例如:

aloha.arachnid.dog.gopher.rat.tiger

注意到,. 两边的字母一定是相同的。

现在给你一些单词,请你找到字典序最小的词链,使得这些单词在词链中出现且仅出现一次。

输入输出格式

输入格式

第一行是一个正整数 \(n\)\(1 \le n \le 1000\)),代表单词数量。

接下来共有 \(n\) 行,每行是一个由 \(1\)\(20\) 个小写字母组成的单词。

输出格式

只有一行,表示组成字典序最小的词链,若不存在则只输出三个星号 ***

输入输出样例

输入样例 #1

6
aloha
arachnid
dog
gopher
rat
tiger

输出样例 #1

aloha.arachnid.dog.gopher.rat.tiger

说明

  • 对于 \(40\%\) 的数据,有 \(n \leq 10\)
  • 对于 \(100\%\) 的数据,有 \(n \leq 1000\)

分析

此题是一道挺复杂的搜索,推荐一做,挺考验码力。

首先,我们可以把字母当成点,单词当成边。

比如,word,就可以代表 \(w \rightarrow d\) 这条边。然后dfs寻找最小的词链。但是啊同志,\(n \le 1000\),这个复杂度裸爆搜,时间复杂度很危险。

所以我们需要经过一个优化:欧拉路找起点优化

首先,原题中要求词链中的词出现并且仅出现一次,说明题目是让我们找一条欧拉路径。

对于非欧拉回路的欧拉路径,一定满足一个点的出度比入度大1,一个点的入度比出度大1,其余点入度与出度均相等

而对于欧拉回路,一定满足所有点的入度与出度均相等

因此,在找起点的时候,我们可以遍历所有点,如果发现一个出度比入度大1的点,就可以设置其为源点 \(S\),如果发现一个入度比出度大1的点,那么它就是终点 \(E\)

  • 如果在遍历过程中发现两个 \(S\),无满足的词链;
  • 如果在遍历过程中发现两个 \(E\),无满足的词链;
  • 如果在遍历过程中,发现一个点的入度和出度的关系既不是差1又不是相等,无满足的词链;
  • 如果遍历完成后,\(S\)\(E\),无满足的词链;
  • 如果遍历完成后,\(E\)\(S\),无满足的词链;
  • 以上判断结束后,如果真的没有 \(S\),说明这个图为欧拉回路,找一个字典序最小的单词开始dfs即可。

你看,直接判断掉这么多不满足的图,是不是感觉倍赚呀~所以欧拉回路和路径的性质有时真的很有用。

但是这样,就能直接开始dfs了吗?

那,如果这个图不联通呢

哈哈哈没错,还有这种情况,那这个怎么解决呢?

并查集

我们将所有在词首和词末出现的字母都新建一个代表字母的节点,并且如果有边那么就将他们合并,最后检查集合数是否为 \(1\) 即可。如果为 \(1\),说明联通;如果不是,那就没解了。

好了,经过这么多筛选了,没有任何无解情况了,终于可以开始欢乐的dfs了!

dfs前要把所有字符串按照字典序排列好哦。

dfs的话。。实在是没啥好讲的了。直接套模板就行了。

那就上代码吧!

代码

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2020-09-26 13:17:28 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2020-09-26 20:06:22
 */
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>

const int maxn = 1005;
const int maxl = 30;

struct node {
    int v, id;
    std :: string word;
};

std :: vector <node> G[maxl];
int ind[maxl], outd[maxl];
bool letter[maxl], vis[maxn];

int n;
std :: string str[maxn], ans[maxn];

int cti(char c) {
    return c - 'a' + 1;
}

void GG() {
    puts("***");
    exit(0);
}

struct ufs {
    int fa[10086];
    bool valid[10086];
    int siz;

    void init(int n) {
        siz = n;
        for (int i = 1; i <= n; ++i)
            fa[i] = i;
    }

    int find(int x) {
        while (x != fa[x]) x = fa[x] = fa[fa[x]];
        return x;
    }
    
    void unite(int x, int y) {
        fa[find(x)] = find(y);
    }

    bool judge(int x, int y) {
        return find(x) == find(y);
    }

    int get_setnum() {
        int setnum = 0;
        for (int i = 1; i <= siz; ++i)
            if (fa[i] == i && valid[i])
                ++setnum;
        return setnum;
    }
};

void dfs(int u, int step) {
    if (step == n) {
        std :: printf("%s", ans[1].c_str());
        for (int i = 2; i <= n; ++i)
            std :: printf(".%s", ans[i].c_str());
        exit(0);
    }

    for (int i = 0; i < G[u].size(); ++i) {
        node e = G[u][i];
        if (!vis[e.id]) {
            vis[e.id] = true;
            ans[step + 1] = e.word;
            dfs(e.v, step + 1);
            vis[e.id] = false;
        }
    }

    return ;
}

int main() {
    std :: scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        std :: cin >> str[i];
    std :: sort(str + 1, str + 1 + n);

    ufs Gu;
    Gu.init(maxl);

    for (int i = 1; i <= n; ++i) {
        int first = cti(str[i][0]);
        int last = cti(str[i][str[i].length() - 1]);
        ++ind[last];
        ++outd[first];
        
        Gu.valid[last] = true;
        Gu.valid[first] = true;
        if (first != last) {
            if (!Gu.judge(first, last))
                Gu.unite(first, last);
        }

        G[first].push_back((node){last, i, str[i]});
    }

    if (Gu.get_setnum() != 1)
        GG();

    for (int i = 1; i <= 26; ++i)
        letter[i] = Gu.valid[i];

    //puts("Ok");

    int S = 0, E = 0;
    for (int i = 1; i <= 26; ++i) {
        if (!letter[i]) continue;
        if (outd[i] == ind[i] + 1) {
            if (S)
                GG();
            S = i;
        } else if (ind[i] == outd[i] + 1) {
            if (E)
                GG();
            E = i;
        } else if (ind[i] == outd[i]) {
            continue;
        } else {
            GG();
        }
    }

    if ((S && !E) || (!S && E))
        GG();
    
    if (!S) S = cti(str[1][0]);
    
    dfs(S, 0);
    
    return 0;
}

评测记录

评测记录

posted @ 2020-09-26 20:07  东北小蟹蟹  阅读(76)  评论(0编辑  收藏