字符串哈希

字符串哈希

将一串字符串映射为一个唯一对应的整数,将字符串的比较(O(n))化简为(O(1))

哈希:将一个字符串看作一个p进制数再模上q

abcabcdefg
1.2.3.1.2.3.4.5.6.7.
=(1*p^9+2*p^8+3*p^7+1*p^6+2*p^5+3*p^4+4*p^3+5*p^2+6*p^1+7*p^0)%q
=hash值(属于0~q-1)

当p取131、13331 q取2^64 (unsigned long long)时重复率最小

abd
(1.2.3)131
=1*131^2+2*131^1+4*131^0
=hash('abd')

会溢出int 但是若命做ULL 相当于自动取模2^64

一般在使用中我们会存下前缀值

要求hash('abcde')
会先存下 h[0]=0
		hash('a')
		hash('ab')
		hash('abc')
		hash('abcd')=hash('abc')*131+4
所以hash(i)与hash(i+1)之间存在一个递推式hash(i+1)=hash(i)*131+strlen(i+1)

若要求

     L      R
。。。。。。。。。。
h(L-R)=h(R)-h(L-1)*131^(R-L+1)

这里令131^(R-L+1)=p[R-L+1]
//板子
const int maxn=1e6+5,base=131;
char s[maxn];
ULL h[maxn],p[maxn];
ULL gethash(int l,int t)
{
	return h[r]-h[l-1]*p[r-l+1];
}
void init()
{
	p[0]=1;
	for(int i=1;i<=maxn;i++)
		p[i]=p[i-1]*base;
}
int main ()
{
	init();
	scanf("%s",s+1);
	int len=strlen(s+1);
	h[0]=0;
	for(int i=1;i<=len;i++)
		h[i]=h[i-1]*base+str[i]-'a'+1;
	cin>>…………………………
}

例1 https://vjudge.net/problem/HDU-1686

Oulipo

HDU - 1686

#include<iostream>
#include<cstring>
using namespace std;
typedef unsigned long long ULL;
const int maxn=1e4+5,base=131;
char str1[maxn],str2[1000005];
ULL h[1000005],p[1000005],str;
int n1,n2,cnt;
ULL gethash(int l, int r) 
{
	return h[r]-h[l-1]*p[r-l+1];
}
void init()
{
	p[0]=1;
	for(int i=1;i<=1000005-1;i++)
		p[i]=p[i-1]*base;
}
int main()
{
	init();
	int n;
	cin>>n;
	while(n--)
	{
	
		h[0]=0;str=0;cnt=0;
		scanf("%s",str1+1);
		//getchar();
		scanf("%s",str2+1);
		n1=strlen(str1+1);n2=strlen(str2+1);
		for(int i=1;i<=n1;i++) 
			str=str*base+(ULL)str1[i];
		for(int i=1;i<=n2;i++) 
			h[i]=h[i-1]*base+(ULL)str2[i];
		for(int i=1;i<=n2-n1+1;i++) 
			if (str==gethash(i,i+n1-1)) cnt++;
		cout<<cnt<<endl;
	}
	return 0;
}  

例二 https://vjudge.net/problem/POJ-2406

Power Strings

POJ - 2406

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef unsigned long long ULL;
const int maxn=1e6+5,base=131;
int cnt,flag;
char s[maxn];
ULL h[maxn],p[maxn];
void init()
{
	p[0]=1;
	for(int i=1;i<maxn;i++)p[i]=p[i-1]*base;
}
int main ()
{
	init();
	while(scanf("%s",s+1))
	{
		int len=strlen(s+1);
		if(s[1]=='.'&&len==1)break;
		cnt=0;h[0]=0;
		for(int i=1;i<=len;i++)
			h[i]=h[i-1]*base+s[i];
		for(int i=1;i<=len;i++)
		{
			if(len%i!=0)continue;flag=1;
			for(int j=i;j<=len;j+=i)
			{ 
				if(h[j]-h[j-i]*p[i]!=(ULL)h[i])
					{flag=0;break;}
			} 
			if(flag!=0){cnt=i;break;}		
		}
		cout<<len/cnt<<endl;
	}	
	return 0;
}

例三 https://vjudge.net/problem/HDU-4821

String

HDU - 4821

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;
typedef unsigned long long int ULL;
const int maxn=1e5+5,base=131;
int m,l,len,ans;
char s[maxn];
ULL h[maxn],p[maxn];
map<ULL,int>mm;
ULL gethash(int l,int r)
{
    return h[r]-h[l-1]*p[r-l+1];
}
void init()
{
	p[0]=1;
	for(int i=1;i<maxn;i++)p[i]=p[i-1]*base;
}
int main()
{
   	init();
    while(scanf("%d%d",&m,&l)!=EOF)
    {
        cin>>s+1;
        len=strlen(s+1);h[0]=0;ans=0;
        for(int i=1;i<=len;i++)
            h[i]=h[i-1]*base+s[i]-'a'+1;
        for(int i=1;i<=l&&i+m*l-1<=len;i++)
        {
            mm.clear();
            for(int j=i;j<i+m*l;j+=l)
                mm[(ULL)gethash(j,j+l-1)]++;
            if(mm.size()==m)ans++;
            for(int j=i+m*l;j+l-1<=len;j+=l)
            {
           		mm[(ULL)gethash(j,j+l-1)]++;
                mm[(ULL)gethash(j-m*l,j-m*l+l-1)]--;
                if(mm[(ULL)gethash(j-m*l,j-m*l+l-1)]==0)mm.erase((ULL)gethash(j-m*l,j-m*l+l-1));
                if(mm.size()==m)ans++;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
} 
posted @ 2020-07-25 18:43  神奇周一  阅读(200)  评论(0编辑  收藏  举报