图书管理(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\n");
            else
                printf("no\n");
        }

    }
    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\n");
            else
                printf("no\n");
        }

    }
    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\n");
            else
                printf("no\n");
        }
    }
    return 0;
}

Trie(华丽丽的RE&MLE)

本文部分图片来源于网络

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

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

~祝大家编程顺利~

原文地址:https://www.cnblogs.com/llllllpppppp/p/9746749.html

时间: 2024-08-28 19:39:45

图书管理(Loj0034)+浅谈哈希表的相关文章

浅谈-分库分表方案

名词解释 库:database:表:table:分库分表:sharding 数据库架构演变 刚开始我们只用单机数据库就够了,随后面对越来越多的请求,我们将数据库的写操作和读操作进行分离, 使用多个从库副本(Slaver Replication)负责读,使用主库(Master)负责写, 从库从主库同步更新数据,保持数据一致.架构上就是数据库主从同步. 从库可以水平扩展,所以更多的读请求不成问题. 但是当用户量级上来后,写请求越来越多,该怎么办?加一个Master是不能解决问题的, 因为数据要保存一

浅谈数据库用户表结构设计,第三方登录

说起用户表,大概是每个应用/网站立项动工(码农们)考虑的第一件事情.用户表结构的设计,算是整个后台架构的基石.如果基石不稳,待到后面需求跟进了发现不能应付,回过头来反复修改用户表,要大大小小作改动的地方也不少.与其如此,不妨设计用户表之初就考虑可拓展性,争取不需要太多额外代价的情况下一步到位. 先前设计: id username password 用户名加上密码,解决简单需求,留个id作为其他表的外键.当然,那时候密码还可能是明文存储,好点的知道md5. 后来呢,随着业务需求的拓展,要加个用户状

yum程序包管理器浅谈

基于rpm安装程序包时,程序包之间的依赖性太过于复杂和麻烦,开发了yum程序包管理器,可以自行的解决程序包之间的依赖关系,并且一次性安装所有的有依赖关系的程序包,无须繁琐的下载依赖性的程序包,相对于rpm来说,rpm为基础包管理,yum是前端工具.而yum需要一个文件服务的服务器,总共有四种文件服务器:ftp服务,http服务,本地文件目录,NFS服务.yum命令工具可以通过配置文件,指向仓库的位置以及相关的各种配置信息:每个yum命令行可以同时指向多个仓库,仓库间可以优先级等相关的配置. 当y

浅谈MySQL多表操作

字段操作 create table tf1( id int primary key auto_increment, x int, y int ); # 修改 alter table tf1 modify x char(4) default ''; alter table tf1 change y m char(4) default ''; # 增加 mysql>: alter table 表名 add 字段名 类型[(长度) 约束]; # 末尾 eg>: alter table tf1 add

【测试管理_浅谈软件测试的价值及如何做】

最近思考测试人员对于项目的价值,后来归纳为3个阶段: 提测前: 价   值: 促使项目需求.实现功能无遗漏,需求与功能实现保存一致 如何做: 1. 重视文档评审,系统软件一般由多个大的软件模块组成,而各软件模块存在单独的文档,在文档评审时,以需求为参照物,审查文档是否一致 2. 若有条件,建议进行接口测试,避免由于缺少沟通导致的软件问题 3.测试人员认真阅读文档,进行测试用例设计 测试中: 价   值: 1.从客户的角度验证产品与用户期望保证一致 2.测试软件,目标软件在用户使用过程中不会出现问

浅谈软件项目的需求管理

软件项目区别于其它项目的最显著的特征是其不可见性,它不像硬件购销.建筑工程,都是实实在在可见的东西.而软件项目在系统交付之前很长一段时间,客户是无法感知自己想要的系统究竟是什么样子.因此,需求管理就显得十分重要,据相关统计数据分析,软件项目90%以上失败的原因都在于没有重视需求或者需求管理方面做的不到位导致的. 需求管理作为软件项目管理的一个重要内容,贯穿项目实施的全生命周期.俗话说:万事开头难.需求作为软件开发的第一个环节,其重要性不言而喻.市面上关于需求管理的相关理论和书籍很多,但多数停留在

浅谈IM软件业务知识—会话session的概念,附一张IM软件的层次图

session一般出现在计算机领域,IM软件中的session,老的IM有两层:首先是逻辑层的session来管理会话的参与者,消息列表,会话类型等等:还有协议层的session,主要是代表客户端跟服务器的一个事物通道. 老的IM软件 客户端跟Server交互的每一类操作都是基于会话.比如客户端登录,需要建立一个登录的会话:客户端发消息,需要建立一个会话.下面举例: 客户端向Server发了一条消息,这条消息的发送就建立在会话之上.客户端需要下面几个步骤. 1. 创建一个session ID=1

浅谈算法和数据结构: 十一 哈希表

在前面的系列文章中,依次介绍了基于无序列表的顺序查找,基于有序数组的二分查找,平衡查找树,以及红黑树,下图是他们在平均以及最差情况下的时间复杂度: 可以看到在时间复杂度上,红黑树在平均情况下插入,查找以及删除上都达到了lgN的时间复杂度. 那么有没有查找效率更高的数据结构呢,答案就是本文接下来要介绍了散列表,也叫哈希表(Hash Table) 什么是哈希表 哈希表就是一种以 键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值. 哈希的思路很简单

浅谈数据结构:哈希表

一.  基本概念 哈希表(hash table )是一种根据关键字直接访问内存存储位置的数据结构,通过哈希表,数据元素的存放位置和数据元素的关键字之间建立起某种对应关系,建立这种对应关系的函数称为哈希函数 二.哈希表的构造方法 假设要存储的数据元素个数是n,设置一个长度为m(m > n)的连续存储单元,分别以每个数据元素的关键字Ki(0<=i<=n-1)为自变量,通过哈希函数hash(Ki),把Ki映射为内存单元的某个地址hash(Ki),并将数据元素存储在内存单元中 从数学的角度看,哈