哈希表入门练习题1

哈希表入门练习题

练习题1 P3370 【模板】字符串哈希

选自洛谷P3370 【模板】字符串哈希 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

如题,给定 N N N 个字符串(第 i i i 个字符串长度为 M i M_i Mi,字符串内包含数字、大小写字母,大小写敏感),请求出 N N N 个字符串中共有多少个不同的字符串。

友情提醒:如果真的想好好练习哈希的话,请自觉。

输入格式

第一行包含一个整数 N N N,为字符串的个数。

接下来 N N N 行每行包含一个字符串,为所提供的字符串。

输出格式

输出包含一行,包含一个整数,为不同的字符串个数。

样例 #1

样例输入 #1
5
abc
aaaa
abc
abcc
12345
样例输出 #1
4

提示

对于 30 % 30\% 30% 的数据: N ≤ 10 N\leq 10 N10 M i ≈ 6 M_i≈6 Mi6 M m a x ≤ 15 Mmax\leq 15 Mmax15

对于 70 % 70\% 70% 的数据: N ≤ 1000 N\leq 1000 N1000 M i ≈ 100 M_i≈100 Mi100 M m a x ≤ 150 Mmax\leq 150 Mmax150

对于 100 % 100\% 100% 的数据: N ≤ 10000 N\leq 10000 N10000 M i ≈ 1000 M_i≈1000 Mi1000 M m a x ≤ 1500 Mmax\leq 1500 Mmax1500

样例说明:

样例中第一个字符串(abc)和第三个字符串(abc)是一样的,所以所提供字符串的集合为{aaaa,abc,abcc,12345},故共计4个不同的字符串。

Tip:
感兴趣的话,你们可以先看一看以下三题:

BZOJ3097:http://www.lydsy.com/JudgeOnline/problem.php?id=3097

BZOJ3098:http://www.lydsy.com/JudgeOnline/problem.php?id=3098

BZOJ3099:http://www.lydsy.com/JudgeOnline/problem.php?id=3099

如果你仔细研究过了(或者至少仔细看过AC人数的话),我想你一定会明白字符串哈希的正确姿势的_


我的解答

我不会,npy教滴TAT

大致思想是,输入字符串时,计算该字符串的hash值,并将该字符串hash值与之前所有的字符串hash值比较,如果不重复则加入数组中,重复就不加入。

我的代码如下

#include <iostream>
#include <string.h>
using namespace std;

const int N = 1e4 + 10, M = 1500, P = 131;

char str[N][M];

int sum, n, h[N], p[N];

int getHash(int i) {
    int len = strlen(str[i] + 1);
    for(int j = 1; j <= len; j ++) {
        p[j] = p[j - 1] * P + str[i][j];
    }
    return p[len];
}

int main () {
    scanf("%d", &n);
    for(int i = 0; i < n; i ++ ) {
        scanf("%s", str[i] + 1);
        
        // 计算该字符串的hash值
        //并将该hash值与之前的字符串hash值比较
        //如果不重复则加入h数组,重复则不加入
        int hash = getHash(i); //获取第i个字符串的hash值
        bool re = false;
        for(int j = 0; j < i; j ++ ) {
            if(h[j] == hash) {
                re = true;
                break;
            }
        }
        if(!re) h[sum ++] = hash;
    }
    printf("%d", sum);
    
}

npy的代码如下:(按我的要求写了很多注释)

#include<iostream>
#include<string.h>

using namespace std;

typedef unsigned long long ULL;

const int N = 10010, M = 1510, P = 131;

char str[N][M];                                   // 用一个二维数组存储所有字符串,字符串最多10000个,最长1500,总计15MB左右

ULL h[N], res[M];                                 // h用来存放所有的不重复结果,每次对一个新的str哈希之后都要从头到尾遍历一次h,复杂度
                                                  // 至多n^2=1e8,不会超时,res数组存放每次处理的str的过程中的哈希值,如果要保存全部
                                                  // 信息需要8*15MB=120MB,爆内存,所以使用一个数组存储,每轮处理str复用内存。
int n, length[N], p;

int main(){
    scanf("%d", &n);                              // 读入字符串个数
    // int Max = 0;
    for(int i = 0; i < n; i++){
        scanf("%s", str[i] + 1);                  // 从下标为1读如str
        length[i] = strlen(str[i] + 1);           // 存储长度,后续计算hash用
        // Max = length[i] > Max ? length[i] : Max;  // 存储最长的长度,欸,这步好像没用
    }
    for(int i = 0; i < n; i++){
        int len = length[i];                      // 读取长度
        for(int j = 1; j <= len; j++){            // 计算哈希
            res[j] = res[j - 1] * P + str[i][j];
        }
        ULL now = res[len];                       // 得到最终hash
        bool flag = true;                         // 看与前面的字符串的hash是否重复
        for(int k = 0; k < p; k++){               
            if(h[k] == now){
                flag = false;
                break;
            }
        }                                           
        if(flag){                                 // 若不重复则记录下来
            h[p++] = now;
        }
    }
    printf("%d\n", p);                            // 输出数组长度
}

练习题2 P2957 [USACO09OCT] Barn Echoes G

传送门:[P2957 USACO09OCT] Barn Echoes G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

The cows enjoy mooing at the barn because their moos echo back, although sometimes not completely. Bessie, ever the excellent

secretary, has been recording the exact wording of the moo as it goes out and returns. She is curious as to just how much overlap there is.

Given two lines of input (letters from the set a…z, total length in the range 1…80), each of which has the wording of a moo on it, determine the greatest number of characters of overlap between one string and the other. A string is an overlap between two other strings if it is a prefix of one string and a suffix of the other string.

By way of example, consider two moos:

moyooyoxyzooo
yzoooqyasdfljkamo

The last part of the first string overlaps ‘yzooo’ with the first part of the second string. The last part of the second string

overlaps ‘mo’ with the first part of the first string. The largest overlap is ‘yzooo’ whose length is 5.

POINTS: 50

奶牛们非常享受在牛栏中哞叫,因为她们可以听到她们哞声的回音。虽然有时候并不能完全听到完整的回音。Bessie 曾经是一个出色的秘书,所以她精确地纪录了所有的哞叫声及其回声。她很好奇到底两个声音的重复部份有多长。

输入两个字符串(长度为 1 1 1 80 80 80 个字母),表示两个哞叫声。你要确定最长的重复部份的长度。两个字符串的重复部份指的是同时是一个字符串的前缀和另一个字符串的后缀的字符串。

我们通过一个例子来理解题目。考虑下面的两个哞声:

moyooyoxyzooo
yzoooqyasdfljkamo

第一个串的最后的部份 yzooo 跟第二个串的第一部份重复。第二个串的最后的部份 mo 跟第一个串的第一部份重复。所以 yzooomo 都是这 2 2 2 个串的重复部份。其中,yzooo 比较长,所以最长的重复部份的长度就是 5 5 5

输入格式

* Lines 1…2: Each line has the text of a moo or its echo

输出格式

* Line 1: A single line with a single integer that is the length of the longest overlap between the front of one string and end of the other.

样例 #1

样例输入 #1
abcxxxxabcxabcd 
abcdxabcxxxxabcx
样例输出 #1
11

提示

‘abcxxxxabcx’ is a prefix of the first string and a suffix of the second string.


我的解答

这道题也是来练练手。因为只有两个字符串,所以可以借鉴上一题的思路,开一个char str[2][N];来存储字符串。直接分别从头从尾计算hash值比较就可以了。要注意下标。

#include <iostream>
#include <string.h>
using namespace std;

typedef unsigned long long ULL;

const int N = 83, P = 131;
char str[2][N];
ULL p[N];
int getHash (int i, int l, int r) {
    int len = r - l + 1;
    p[l - 1] = 0;
    for(int j = l; j <= r; j ++ ) {
        p[j] = p[j-1] * P + str[i][j]; 
    }
    return p[r];
}

int main() {
    scanf("%s%s", str[0] + 1, str[1] + 1);
    int ans = 0;
    int len1 = strlen(str[0] + 1);
    int len2 = strlen(str[1] + 1);
    int len = min(len1, len2); 
    // 遍历第一个字符串前缀和第二个字符串后缀
    // str[0]: 1 i    str[1]:   len2 - i + 1, len2
    for(int i = 1; i <= len; i ++ ) {
        if(getHash(0, 1, i) == getHash(1, len2 - i + 1, len2)) {
            ans = max(ans, i);
        }
        if(getHash(1, 1, i) == getHash(0, len1 - i + 1, len1)) {
            ans = max(ans, i);
        }
    }
    printf("%d", ans);
    return 0;
}
  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值