zoukankan      html  css  js  c++  java
  • 51nod-1526-贪心+Trie

    题目链接在这

    题目来源: CodeForces
    基准时间限制:1 秒 空间限制:131072 KB 分值: 320 难度:7级算法题
    收藏
    关注

    班里有n个同学。老师为他们选了n个笔名。现在要把这些笔名分配给每一个同学,每一个同学分配到一个笔名,每一个笔名必须分配给某个同学。现在定义笔名和真名之间的相关度是他们之间的最长公共前缀。设笔名为a,真名为b,则他们之间的相关度为lcp(a,b)。那么我们就可以得到匹配的质量是每一个同学笔名和真名之间相关度的和。

    现在要求分配笔名,使得匹配质量最大。

    样例解释:

    ·        bill → bilbo (lcp = 3)

    ·        galya → galadriel (lcp = 3)

    ·        gennady → gendalf (lcp = 3)

    ·        toshik → torin (lcp = 2)

    ·        boris → smaug (lcp = 0)

    Input
    单组测试数据。
    第一行有一个整数n (1≤n≤100000),表示班级中同学的数目。
    接下来n行,表示每一个同学的真名,每一个名字是非空串,且由小写字母组成。
    名字可能重复。
    最后n行是老师已经安排好的笔名。每一个笔名是一个非空串,且由小写字母组成。
    笔名可能重复。
    输入的字符总数目不超过 800000。
    Output
    输出最大的匹配质量。
    Input示例
    样例输入1
    5
    gennady
    galya
    boris
    bill
    toshik
    bilbo
    torin
    gendalf
    smaug
    galadriel
    Output示例
    样例输出1
    11
    很容易想到一个贪心的算法是,对于每一个笔名,把他用在使得他贡献最大的人身上,字符串前缀最大,数据量又很大,考虑使用字典树把人名存下,用笔名在树上匹配。思路是对的但出了一个bug。
    如果在某个称号还未配对时,他的最优人被其他人用了咋整。可以这样想,每个结点统计前缀个数vis[u],匹配时只要vis[u]存在就让ans++;累计一个贡献,最后就是正解。假如这个前缀有多个,那就是
    按贡献大小给占用他的人,如果个数有限的话,那么势必会有一些名称的匹配贡献是0,我们当然要让匹配的少的那些为零。这个过程在代码里不会呈现但不会影响最终结果。
    假如有笔名: abxx abccc 有人名: abccx 有且仅有一个
    当abxx先找时,相当于贡献了'a','b'两个贡献,余下的不再匹配树上这两个字符对应节点的价值也会减一(归零),当abccc进行匹配时,会做出'c','c'两个贡献
    这样一共进行了四个贡献,虽然看起来abccc前缀并没有完全匹配,但只不过是"abxx"提前替他匹配了一小段前缀而已。
     1 #include<bits/stdc++.h>
     2 using  namespace std;
     3 int ch[800010][26];
     4 int sz=1,ans=0;
     5 int vis[800010];
     6 char s[800010];
     7 void insert(char *s){
     8     int u=0,n=strlen(s);
     9     for(int i=0;i<n;++i){
    10         int c=s[i]-'a';
    11         if(!ch[u][c]) ch[u][c]=sz++;
    12         vis[u]++;
    13         u=ch[u][c];
    14     }
    15     vis[u]++;
    16 }
    17 void match(char *s){
    18     int i,u=0,n=strlen(s),r=0,ret=0;
    19     for(i=0;i<n;++i){
    20         int c=s[i]-'a';
    21         if(ch[u][c]==0) return ;
    22         u=ch[u][c];
    23         if(vis[u]){
    24         vis[u]--;
    25         ans++;
    26         }
    27     }
    28 }
    29 int main()
    30 {
    31     int n,m,i,j,k;
    32     cin>>n;
    33         for(i=1;i<=n;++i){
    34             scanf("%s",s);
    35             insert(s);
    36         }
    37         for(i=1;i<=n;++i){
    38             scanf("%s",s);
    39             match(s);
    40         }
    41         printf("%d
    ",ans);
    42     return 0;
    43 }
    44 /*
    45 5
    46 gennady
    47 galya
    48 boris
    49 bill
    50 toshik
    51 
    52 bilbo
    53 torin
    54 gendalf
    55 smaug
    56 galadriel
    57 */
  • 相关阅读:
    JS leetcode 买卖股票的最佳时机 题解分析,我离职了。
    JS leetcode x 的平方根 题解分析
    JS leetcode 有多少小于当前数字的数字 解题分析,你应该了解的桶排序
    JS leetcode 合并两个有序数组 解题分析
    JavaSE部分 (多线程下)
    JavaSE部分 (多线程上)
    JavaSE部分 (异常)
    JavaSE部分 集合下(Map)
    JavaSE部分 集合中(数据结构 list set Collections)
    JavaSE (接口 final 内部类)
  • 原文地址:https://www.cnblogs.com/zzqc/p/8411174.html
Copyright © 2011-2022 走看看