• 图书管理(Loj0034)+浅谈哈希表


    图书管理

    题目描述

    图书管理是一件十分繁杂的工作,在一个图书馆中每天都会有许多新书加入。为了更方便的管理图书(以便于帮助想要借书的客人快速查找他们是否有他们所需要的书),我们需要设计一个图书查找系统。

    该系统需要支持 2 种操作:

    1. add(s) 表示新加入一本书名为 s 的图书。
    2. find(s) 表示查询是否存在一本书名为 s 的图书。

    输入格式

    第一行包括一个正整数 n,表示操作数。 以下 n 行,每行给出 2 种操作中的某一个指令条,指令格式为:

    add s
    find s

    在书名 s 与指令(addfind)之间有一个隔开,我们保证所有书名的长度都不超过 200。可以假设读入数据是准确无误的。

    输出格式

    对于每个 find(s) 指令,我们必须对应的输出一行 yes 或 no,表示当前所查询的书是否存在于图书馆内。

    注意:一开始时图书馆内是没有一本图书的。并且,对于相同字母不同大小写的书名,我们认为它们是不同的。

    样例

    样例输入

    4
    add Inside C#
    find Effective Java
    add Effective Java
    find Effective Java

    样例输出

    no
    yes

    数据范围与提示

    n≤30000

    借这个题说几件事情

    (1)输入

    我们发现书名中间是有空格的,cin和scanf一旦遇到空格就会断开

    所以这里要用gets()(详见代码)

    (2)双hash

    通常来说,hash发生碰撞的概率比较大,所以我们可以分别取两个乘数和模数,只有两个hash值均相等时才说两个数相等。这样可以大大减少碰撞的概率。

    (3)哈希表

    一种数据结构。查找hash表的时间近似于O(n),十分便捷。

    比如我们如果要存一个数组并查找

    如果用朴素的A[1……n]来存储

    即使用二分查找也要O(logN)

    如果用hash的思想,可以将hash值相同的几个数用链表存在一起,每个hash值开一个链表(其实就是邻接表

    这个东西就叫哈希表

    如图,这是一个模数为16的哈希表(实际上将16选为模数并不合适)

    储存和查找的期望复杂度为O(1),实际的复杂度取决于链表长度(也就是你选的模数好不好、数据友善不友善),可以看成一个常数

    给出代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define ULL unsigned long long
    #define P 37
    #define P2 97
    #define MOD 100003
    #define MOD2 100009
    using namespace std;
    
    inline int read()
    {
        int f=1,x=0;
        char ch=getchar();
        while(ch<'0' || ch>'9') {if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int n,cnt;
    int v[MOD2+5],head[MOD2+5],nxt[MOD2+5];
    char a[10],c[205];
    int i,j;
    
    void add(int x1,int x2)//哈希表(怎么看都是邻接表……)
    {
        v[++cnt]=x2;
        nxt[cnt]=head[x1];
        head[x1]=cnt;
        return ;
    }
    
    bool check(int x1,int x2)
    {
        for(int k=head[x1];k!=-1;k=nxt[k])
        {
            if(v[k]==x2) return 1;
        }
        return 0;
    }
    
    int main()
    {
        memset(head,-1,sizeof(head));
        n=read();
        for(i=1;i<=n;i++)
        {
            scanf("%s",a);
            gets(c);
            int len=strlen(c);
            int sum1=0,sum2=0;
            for(j=0;j<len;j++)
            {
                sum1=(sum1*P+c[j])%MOD;
                sum2=(sum2*P2+c[j])%MOD2;//使用双hash值,减少碰撞概率
            }
            if(a[0]=='a') add(sum1,sum2);
            else
            {
                if(check(sum1,sum2))
                    printf("yes
    ");
                else
                    printf("no
    ");
            }
            
        }
        return 0;
    }
    哈希表(标准答案)

    此外还有其他方法:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define ULL unsigned long long
    #define P 1000000007
    using namespace std;
    
    inline int read()
    {
        int f=1,x=0;
        char ch=getchar();
        while(ch<'0' || ch>'9') {if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int n,cnt;
    ULL v[30005];
    char a[10],c[205];
    int i,j;
    
    void add(int x)
    {
        v[++cnt]=x;
    }
    
    bool check(int x)
    {
        for(int k=1;k<=cnt;k++)
        {
            if(v[k]==x) return 1;
        }
        return 0;
    }
    
    int main()
    {
        n=read();
        for(i=1;i<=n;i++)
        {
            scanf("%s",a);
            gets(c);
            int len=strlen(c);
            ULL sum=0;
            for(j=0;j<len;j++)
            {
                sum=sum*P+c[j];
            }
            if(a[0]=='a') add(sum);
            else
            {
                if(check(sum))
                    printf("yes
    ");
                else
                    printf("no
    ");
            }
            
        }
        return 0;
    }
    朴素hash(容易被卡+TLE)

    石乐志的尝试↓:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    inline int read()
    {
        int f=1,x=0;
        char ch=getchar();
        while(ch<'0' || ch>'9') {if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int n,tot;
    char a[10],s[205];
    int ts[900005][300];
    bool book[900005]; 
    int i,j;
    
    void make_trie()
    {
        int len=strlen(s),u=0;
        for(int k=0;k<len;k++)
        {
            int c=s[k];
            if(!ts[u][c])
                ts[u][c]=++tot;
            u=ts[u][c];
        }
        book[u]=1;
    }
    
    bool find_trie()
    {
        int len=strlen(s),u=0;
        for(int k=0;k<len;k++)
        {
            int c=s[k];
            if(!ts[u][c]) return 0;
            u=ts[u][c];
        }
        if(book[u]) return 1;
        else return 0;
    }
    
    int main()
    {
        n=read();
        for(i=1;i<=n;i++)
        {
            scanf("%s",a);
            gets(s);
            if(a[0]=='a')
            {
                make_trie();
            }
            else
            {
                if(find_trie())
                    printf("yes
    ");
                else
                    printf("no
    ");
            }
        }
        return 0;
    }
    Trie(华丽丽的RE&MLE)

    本文部分图片来源于网络

    部分内容参考《信息学奥赛一本通.提高篇》第二部分第一章 哈希和哈希表

    若需转载,请注明https://www.cnblogs.com/llllllpppppp/p/9746749.html

     

    ~祝大家编程顺利~

  • 相关阅读:
    Java 对象初始化
    Java 栈和堆
    值得细品
    磁盘的分区、格式化与挂载
    VirtualBox预存空间不足
    做个备忘
    SQL查数据库有哪些触发器,存储过程...
    SQL 中 CASE
    FMX的Style中的Effects的注意问题
    Python图像处理库(2)
  • 原文地址:https://www.cnblogs.com/llllllpppppp/p/9746749.html
Copyright © 2020-2023  润新知