字符串哈希——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;
}

謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章