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

一.字符串哈希

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;
}

谢谢!

发布了61 篇原创文章 · 获赞 32 · 访问量 8350

猜你喜欢

转载自blog.csdn.net/weixin_43908980/article/details/96758732