Fork me on GitHub

【题解】游戏

原文链接:https://www.cnblogs.com/ctjcalc/p/post1.html

题目描述见Luogu P2462

算法分析

其实这道题并不难,关键是如何转化。因为需要找到最长的单词接龙,就可以用图论来看。单词接龙不会出现环,所以,这就是个`DAG`上的拓扑排序。如果两个单词可以接在一起,就必须满足以下条件:
  • 前一个单词的字母都必须在后一个单词中出现过
  • 任意一个字母都不能少
  • 后一个单词的长度比前一个单词多1,不能多也不能少

因为没有对顺序作要求,我们只需记录其出现次数即可,并存储它们的哈希值(hash/散列),枚举每个字符串的每个字母,增加其出现次数,并判断该字符串是否存在,如果存在,就建一条有向边。

最后,拓扑排序,记录答案并通过前驱指针递归输出。

# 代码实现
#include <bits/stdc++.h>
using namespace std;

#if __cplusplus < 201103 || !defined(__cplusplus)
typedef map<int,int> maptype;
#else
typedef unordered_map<int,int> maptype; // 如果是C++11及以上,使用无序哈希映射
#endif

struct edge
{
    int to,nxt;
};

edge e[1000001]; int head[10001],tot;
int in[10001];
maptype mapping;
char str[10001][105];
int len[10001];
int cnt[10001][26];
int f[10001];
int pre[10001];
int n;

void connect(int x,int y){
    e[++tot]=(edge){y,head[x]}; head[x]=tot; ++in[y];
}

int gethash(int idx){ // 哈希函数
    int val=0;
    for(register int i=0;i<26;++i){
        val=val*23+cnt[idx][i];
    }
    return val;
}

void output(int d){ // 递归输出
    if(pre[d]!=0){
        output(pre[d]);
    }
    printf("%s\n",str[d]+1);
}

void topology(){ //拓扑排序
    queue<int> q;
    for(register int i=1;i<=n;++i){
        if(!in[i]) q.push(i);
        f[i]=1;
    }
    while(!q.empty()){
        int x=q.front(); q.pop();
        for(register int i=head[x],y;y=e[i].to,i;i=e[i].nxt){
            if(f[y]<f[x]+1){
                f[y]=f[x]+1;
                pre[y]=x;
            }
            if(--in[y]==0){
                q.push(y);
            }
        }
    }
    int ans=0;
    for(register int i=1;i<=n;++i){
        if(f[ans]<f[i]){
            ans=i;
        }
    }
    printf("%d\n",f[ans]);
    output(ans);
}

void build(){ //建边
    for(register int i=1;i<=n;++i){
        for(register int j=0;j<26;++j){
            ++cnt[i][j];
            int h=gethash(i);
            if(mapping.find(h)!=mapping.end()){ // 如果存在一个可接的单词就建边
                connect(i,mapping[h]);
            }
            --cnt[i][j]; // 要记得还原
        }
    }
}

void input(){
    while(scanf("%s",str[++n]+1)!=EOF); --n; // 注意输入
    for(register int i=1;i<=n;++i){
        len[i]=strlen(str[i]+1);
        for(register int j=1;j<=len[i];++j){
            ++cnt[i][str[i][j]-'a'];
        }
        mapping[gethash(i)]=i;
    }
}

int main(){
    input();
    build();
    topology();
    return 0;
}
posted @ 2019-10-25 12:45  ctjcalc  阅读(193)  评论(0编辑  收藏  举报