字符串哈希——COCI2017 ​Lozinke ——2019暑假篇

本文详细介绍了字符串哈希算法的基本原理及其应用。首先讲解了哈希初始化公式与字符串截取公式的推导过程,随后通过一道COCI竞赛题实例演示了如何使用字符串哈希解决实际问题,并提供了一份完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.字符串哈希

1.前言

这是一种十分重要的算法,它能够迅速计算出字符串里的存在性问题,代码也并不长,挺好写的

2.表示方式

用数组hash[i]表示字符串i哈希之后的值,用x数组表示原字符串,则有:

hash[i]  =  (  hash[i  -  1]  *  p  +  (  x[i]  -  'a'  +  1  ))  %  mod

p表示一个进制关系,越大越好,这样可以避免重复。最好用unsigned long long,可以自然溢出,它主动模(2^64 - 1)

3.性质

我们这样表示这个字符串:

x[i]  =  s1s2s3......si

那么有:

x[1]  =  s1

x[2]  =  s1s2

x[3]  =  s1s2s3

x[4]  =  s1s2s3s4  

 其中一个字符串的表示方法就是:

sl......sr  =  hash[r]  -  hash[l  -  1]  *  p  ^  (r  -  l  +  1)

可以通过观察证明,好那么字符串哈希最重要的两个公式就介绍完了:

哈希初始化 hash[i]  =  (  hash[i  -  1]  *  p  +  (  x[i]  -  'a'  +  1  ))  %  mod

截取字符串中一段 sl......sr  =  hash[r]  -  hash[l  -  1]  *  p  ^  (r  -  l  +  1)

上例题QWQ

二.典例:COCI2017 ​Lozinke

1.题目

点击打开链接

2.题解 

这道题目在有了字符串哈希的基础上就很简单了吧,再说每个字符串长度不超过10.

现在来说一下详细的实现步骤:

1.初始化哈希每个字符串

2.对哈希出来的值进行排序和去重

3.枚举每一个字符串里的子串,统计每个子串的出现次数

4.枚举每个字符串,看每个字符串出现了多少次,累加出现次数减一

3.Code

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define M 20005
#define P 117
#define ULL unsigned long long

ULL p[20], lisan[M * 100], vap[M][20];
int n, len[M], tot, vis[M * 100], cnt[M * 100], ans;
char a[M][15];

ULL get_hash (int l, int r, int cur){
	return vap[cur][r] - vap[cur][l - 1] * p[r - l + 1];
}
int main (){
	scanf ("%d", &n);
	p[0] = 1;
	for (int i = 1; i <= 10; i ++)
		p[i] = p[i - 1] * P;
	for (int i = 1; i <= n; i ++){
		scanf ("%s", a[i] + 1);
		len[i] = strlen (a[i] + 1);
		for (int j = 1; j <= len[i]; j ++)
			vap[i][j] = vap[i][j - 1] * P + (a[i][j] - 'a' + 1);
		for (int j = 1; j <= len[i]; j ++){
			for (int k = j; k <= len[i]; k ++){
				lisan[++ tot] = get_hash (j, k, i);
			}
		}
	}
	sort (lisan + 1, lisan + 1 + tot);
	int tmp = unique (lisan + 1, lisan + 1 + tot) - lisan;
	tot = tmp;
	for (int i = 1; i <= n; i ++){
		for (int j = 1; j <= len[i]; j ++){
			for (int k = j; k <= len[i]; k ++){
				int now = lower_bound (lisan + 1, lisan + 1 + tot, get_hash (j, k, i)) - lisan;//找位置,离散化
				if (vis[now] != i)
					vis[now] = i, cnt[now] ++;
			}
		}
	}
	for (int i = 1; i <= n; i ++){
		int now = lower_bound (lisan + 1, lisan + 1 + tot, get_hash (1, len[i], i)) - lisan;
		ans += cnt[now] - 1;
	}
	printf ("%d\n", ans);
	return 0;
}

谢谢!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值