Hash~

简介: Hash~

image.jpeg


优势:任意给定一段区间[L,R]可以用O(1)的时间算出来

#include <iostream>
#include <string.h>
using namespace std;
typedef unsigned long long ull;
const int N = 1000010;
const int base = 131;
char str[N];
ull h[N];
int main()
{
    scanf("%s",str + 1);
    //算每个前缀的哈希值
    int n = strlen(str + 1);
    for(int i = 1;i <= n;i++)
    {
        h[i] = h[i - 1] * base + str[i] - 'a' + 1;
    }
    for(int i = 1;i <= n;i++)
    {
        printf("%llu\n",h[i]);
    }
    return 0;
}
//abc
//1
//133
//17426

算任意两段的哈希值的代码:

求某两段是不是一样的,分别求hash值判等

#include <iostream>
#include <string.h>
using namespace std;
typedef unsigned long long ull;
const int N = 1000010;
const int base = 131;
char str[N];
ull h[N],p[N];
ull get(int l,int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
    scanf("%s",str + 1);
    //算每个前缀的哈希值
    int n = strlen(str + 1);
    p[0] = 1;
    for(int i = 1;i <= n;i++)
    {
        h[i] = h[i - 1] * base + str[i] - 'a' + 1;
        p[i] = p[i - 1] * base;
    }
    int m;
    cin>>m;
    while(m--)
    {
        int l,r;
        cin>>l>>r;
        cout<<get(l,r)<<endl;
    }
    return 0;
}
/*
abcabcabc
3
1 3
17426
4 6
17426
7 9
17426
*/
兔子与兔子

60.png

由于数据比较大,接收时选择scanf,与上面代码就这不一样。【效率快】

#include <iostream>
#include <string.h>
using namespace std;
typedef unsigned long long ull;
const int N = 1000010;
const int base = 131;
char str[N];
ull h[N],p[N];
ull get(int l,int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
    scanf("%s",str + 1);
    //算每个前缀的哈希值
    int n = strlen(str + 1);
    p[0] = 1;
    for(int i = 1;i <= n;i++)
    {
        h[i] = h[i - 1] * base + str[i] - 'a' + 1;
        p[i] = p[i - 1] * base;
    }
    int m;
    cin>>m;
    while(m--)
    {
        int l1,r1,l2,r2;
        scanf("%d %d %d %d",&l1,&r1,&l2,&r2);
        if(get(l1,r1) == get(l2,r2))
            puts("Yes");
        else
            puts("No");
    }
    return 0;
}
回文子串的最大长度

上海自来水来自海上

61.png

左边的正序 == 右边的逆序

首先,回文串分为两大类:长度是奇数abcba,长度是偶数abba。

①技巧

②枚举中点

③二分半径,一个长度,如果一样就扩大长度,否则缩小长度。


技巧:

abcba --> a#b#c#b#a 奇数个数

abba --> a#b#b#a 也变成奇数个数

s个字母 补上 s-1个字母 变成 2s - 1 (奇数个呗)

#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
typedef unsigned long long ULL;
const int N = 2000010, base = 131;
char str[N];
ULL hl[N], hr[N], p[N];
ULL get(ULL h[], int l, int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
    int T = 1;
    while (scanf("%s", str + 1), strcmp(str + 1, "END")) //调用这个函数,判断有没有遇到END,如果有就结束。逗号表达式,用逗号隔开的语句,返回值,只看最后一个语句的返回值 **细节**
    {
        int n = strlen(str + 1);
        for (int i = n * 2; i > 0; i -= 2)
        {
            str[i] = str[i / 2];
            str[i - 1] = 'z' + 1;
        }
        n *= 2;
        p[0] = 1;
        for (int i = 1, j = n; i <= n; i ++, j -- )//i,j分别是正序,逆序
        {
            hl[i] = hl[i - 1] * base + str[i] - 'a' + 1;
            hr[i] = hr[i - 1] * base + str[j] - 'a' + 1;
            p[i] = p[i - 1] * base;
        }
        int res = 0;
        for (int i = 1; i <= n; i ++ )
        {
            int l = 0, r = min(i - 1, n - i);
            while (l < r)
            {
                int mid = l + r + 1 >> 1;
                if (get(hl, i - mid, i - 1) != get(hr, n - (i + mid) + 1, n - (i + 1) + 1)) r = mid - 1;//注意端点值的确定,自己举俩例子判断找的对不对
                else l = mid;
            }
            if (str[i - l] <= 'z') res = max(res, l + 1);
            else res = max(res, l);
        }
        printf("Case %d: %d\n", T ++, res);
    }
    return 0;
}
后缀数组

62.png

63.png

/*
ponoiiipoi
10 i
9 oi
8 poi
7 ipoi
6 iipoi
5 iiipoi
4 oiiipoi
3 noiiipoi
2 onoiiipoi
1 ponoiiipoi
输出字典序最小的……
输出排名为 i 的后缀与排名为 i-1 的后缀,把二者的最长公共前缀的长度记为 Height[i] 
*/ 
#include <iostream>
#include <algorithm>
#include <string.h>
#include <limits.h>
using namespace std;
typedef unsigned long long ULL;
const int N = 300010, base = 131;
int n;
char str[N];
ULL h[N], p[N];
int sa[N];
int get(int l, int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}
int get_max_common_prefix(int a, int b)
{
    int l = 0, r = min(n - a + 1, n - b + 1);
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (get(a, a + mid - 1) != get(b, b + mid - 1)) r = mid - 1;
        else l = mid;
    }
    return l;
}
bool cmp(int a, int b)
{
    int l = get_max_common_prefix(a, b);
    int av = a + l > n ? INT_MIN : str[a + l];
    int bv = b + l > n ? INT_MIN : str[b + l];
    return av < bv;
}
int main()
{
    scanf("%s", str + 1);
    n = strlen(str + 1);
    p[0] = 1;
    for (int i = 1; i <= n; i ++ )
    {
        h[i] = h[i - 1] * base + str[i] - 'a' + 1;
        p[i] = p[i - 1] * base;
        sa[i] = i;
    }
    sort(sa + 1, sa + 1 + n, cmp);
    for (int i = 1; i <= n; i ++ ) printf("%d ", sa[i] - 1);
    puts("");
    for (int i = 1; i <= n; i ++ )
        if (i == 1) printf("0 ");
        else printf("%d ", get_max_common_prefix(sa[i - 1], sa[i]));
    puts("");
    return 0;
}
所有和前缀和有关的下标都从1开始!
相关文章
|
6天前
|
存储 负载均衡 算法
Hash介绍与应用详解
哈希算法在计算机科学中有着广泛而重要的应用,从数据存储、数据完整性校验到密码安全和分布式系统中的负载均衡,哈希函数都发挥着关键作用。通过本文的介绍和示例代码,希望您能更好地理解哈希的基本概念和实际应用,并在您的项目中有效地应用这些知识。
24 3
|
5月前
|
存储 缓存 搜索推荐
Hash Table
【6月更文挑战第12天】
31 1
|
6月前
|
Shell
|
存储 算法 安全
Hash 算法详细介绍与实现 (二)
书接上回,昨天写了第一部分,《Hash 算法详细介绍与实现 (一)》详细介绍了Hash表和Hash算法的相关概念以及算法的基本原理。同时简单介绍几种hash算法的实现:直接取余法和乘法取整法;本文接着详细唠唠Hash算法和Hash表这个数据结构的具体实现以及Hash算法和Hash表常见问题的解决方案,比如解决Hash表的冲突问题等等.相关的理论知识已在上篇文章详细介绍,这里就不再赘述,多的不说少的不唠,直接进入今天的主题:利用Hash算法实现Hash表
483 1
|
数据安全/隐私保护 Python
散列函数(Hash Function)
散列函数(Hash Function)是一种将任意大小的数据映射到固定大小的数据的函数。通常,散列函数将输入数据转换成固定长度的输出,称为散列值(Hash Value),散列值通常是一串数字和字母组成的固定长度的字符串。散列函数可以用于数据加密、数据完整性检查、数据压缩等方面。
145 1
|
存储 算法
|
存储 算法
hash
一.什么是hash 百度百科上的定义是: 是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
103 0
|
前端开发 JavaScript
hash、chunkhash和contenthash
webpack 通用配置优化
126 0
hash、chunkhash和contenthash
|
存储 NoSQL Redis
|
存储 算法 安全
什么是 Hash 算法?
散列算法(Hash Algorithm),又称哈希算法,杂凑算法,是一种从任意文件中创造小的数字「指纹」的方法。与指纹一样,散列算法就是一种以较短的信息来保证文件唯一性的标志,这种标志与文件的每一个字节都相关,而且难以找到逆向规律。因此,当原有文件发生改变时,其标志值也会发生改变,从而告诉文件使用者当前的文件已经不是你所需求的文件。
什么是 Hash 算法?