• 【题解】游戏


    原文链接: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
    ",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
    ",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;
    }
    
  • 相关阅读:
    vue--常用指令
    vue--npm的使用
    DRF--认证和权限
    DRF--路由组件和版本控制
    nginx--代理和负载均衡
    DRF--重写views
    DRF--ModelSerializer和时间格式化
    DRF--验证器
    DRF--序列化
    DRF--介绍和安装
  • 原文地址:https://www.cnblogs.com/ctjcalc/p/post1.html
Copyright © 2020-2023  润新知