KMP & 字符串哈希

KMP

最关键的就是求next数组:

其实我们kmp的思想就是利用之前匹配成功的串,减少匹配次数,以降低时间复杂度;

简单来说就是一个最大前缀和一个最大后缀匹配的问题。

当我们在匹配过程中,匹配失败时,我们可以根据next数组,快速定位以上一个匹配成功的字符结尾的最大后缀。

KMP & 字符串哈希_第1张图片

我们时刻要想着我们求的是最大前缀和最大后缀相同,所以,上一次匹配成功后,我们拿上一次匹配成功的后一个字符和当前字符进行比较;

看看是否一致,如果不一致的话,我们则后退一步,如果还是不一致的话,那我们就接着退,直到退无可退;

如果一致后,那我们令我们的匹配的最大前后缀+1;因为我们没错是拿后面的和前面的匹配,所以我们的数组下标一定要从一开始,否则会越界;

 【模板】KMP字符串匹配 - 洛谷

#include
using namespace std;

const int N = 1000010;

string s = " ",t = " ";

int border[N];

int main()
{
	string a , b;
	
	cin >> a >> b;
	s += a;
	t += b;
	
	int n = s.size();
	int m = t.size();
	
    //求next数组
	for(int i =2 ,j = 0 ;i <= m; i++)
	{
		while(j && t[i] != t[j+1]) j = border[j]; 
		if(t[i] == t[j+1]) j++;
		border[i] = j;  
	}
	
    //进行匹配
	for(int i = 1 ,j = 0 ;i<= n ; i++)
	{
		while(j && s[i] != t[j+1]) j = border[j];
		if(s[i] == t[j+1]) j++;
		if(j == m - 1)
		{
			cout<

字符串哈希

字符串哈希说白了就是,将一个字符串映射成一个数字,这样我们每次询问的话都是O(1)的复杂度,接下来我们先来看看如何对字符串进行映射;

KMP & 字符串哈希_第2张图片

我们获取到一个字符串后,将该字符串看成是一个P进制的数;

我们知道如何将二进制转化成十进制;同理我们将一个字符串转化成P进制;

KMP & 字符串哈希_第3张图片

 这里我选取对应索引的ASCII作为P进制的乘数;这个数可以随便选取,前提是保证唯一性不为零

我们将上述字符串转化:(101 * P^0 + 99 *P^1 + 105 * P^2 + 118 * P^3 + 111 * P ^4 + 110* P ^5)mod Q

因为这个数算出来的话会很大,所以我们这里 mod 上一个大质数(为什么是质数,这个和数论有关)这样的话,我们就把一个字符串映射成了一个数。

(这里有人会问,会不会出现冲突,答案是很肯定的,一定会出现冲突,除非我不卡你,这是不可能的);

 那有什么用呢,我们可以对字符串进行询问,查看子串匹配问题等等问题;

那接下来我们就来看看如何求一个子串的hash值呢;

下面我们要求从L到R的hash值;

我们知道应该应 1-R 的hash值减去 1 - (L - 1)  的hash值;

我们考虑直接减去的答案对吗,显然是错误的;

因为两者的起点不一样;一个从 R 开始,一个从 L - 1 开始;

KMP & 字符串哈希_第4张图片

这里我们举一个简单的例子:

123456789:(L ~R) = (7 ~ 9) 

我们要获得789 能直接用123456789 - 12456吗;

不能;我们要用123456789 - 123456000;

我们要把(1 ~ L - 1) hash值乘上一个 P^{R-L+1},这样是不是就对了;

【模板】字符串哈希 - 洛谷

#include
#include
#include
#include
#define x first
#define y second 
#define _for(i,s,t) for(int i = (s);i <=t ; i++)
using namespace std;

typedef unsigned long long ULL;
typedef long long LL;
typedef pair PII;

const int N = 100010 ,base = 131,P = 9999971;

int n;

int h[N],idx;

int main()
{
	cin>>n;
	_for(i,1,n)
	{
		string str;
		cin>>str;
		int num = 0;
		int m = str.size();
		for(int j =0 ;j< m ;j++)
		{
			num = (num * base +str[j]) % P;
		} 
		h[idx] = num,idx++;
	}
	sort(h , h + idx);
	 
	int res = 0;
	
	for(int i =0 ;i<= n ;i++)
	{
		if(h[i]!=h[i+1]) res++;
	}
	
	cout<

我们根据刚才学的一顿操作完后,喜提一发WA;我们人品不太行,被卡了;

既然我们用一个hash映射不行,那我们就用两个 ------- 双哈希;

所谓双哈希就是我们映射两次;

#include
#include
#include
#include
#define x first
#define y second 
#define _for(i,s,t) for(int i = (s);i <=t ; i++)
using namespace std;

typedef unsigned long long ULL;
typedef long long LL;
typedef pair PII;

const int N = 100010 ,base1 = 131,P1 = 9999971,base2 = 101 , P2 = 9999973;

int n;

int h1[N],h2[N],idx;

PII hah[N];

int main()
{
	cin>>n;
	_for(i,1,n)
	{
		string str;
		cin>>str;
		int num1 = 0, num2 = 0 ;
		int m = str.size();
		for(int j =0 ;j< m ;j++)
		{
			num1 = (num1 * base1 +str[j]) % P1;
			num2 = (num2 * base2 + str[j]) % P2;
		} 
		h1[idx] = num1 , h2[idx] = num2 , idx++;
	}
	
	for(int i =0 ;i< idx; i++)
	{
		hah[i] = {h1[i],h2[i]};
	}
	
	sort(hah, hah + idx);
	
	int res = 0;
	
	for(int i =0 ;i<= n ;i++)
	{
		int a1 = hah[i].x ,a2 = hah[i+1].x;
		int b1 = hah[i].y ,b2 = hah[i+1].y;
		
		if(a1 != a2 || b1 != b2) res++;
	}
	
	cout<

Novice on the way !

你可能感兴趣的:(哈希算法,算法)