暖*墟#Hash# 字符串Hash与例题
Posted floraloveryuuji
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暖*墟#Hash# 字符串Hash与例题相关的知识,希望对你有一定的参考价值。
【字符串Hash】
1.特征与理解
用于寻找字符组出现的位置或次数的问题,即【字符串匹配问题】。
2.滚动哈希的优化技巧
选取两个合适的互质数b,h(b<h),假设字符串C=c1c2...cm,
定义哈希函数为:H(C)=(c1*b^(m-1)+c2*b^(m-2)+....+cm*b^0) mod h。
b为基数,H(C)的处理相当于把字符串看成b进制数。
这一过程通过递归计算:H(C,k)=H(C,k-1)*b+ck。
判断某段字符与另一匹配串是否匹配,即判断:
(某段字符:从位置k+1开始的长度为n的子串C‘=ck+1ck+2....ck+n)
H(C‘)=H(C,k+n)-H(C,k)*b^n 与 H(S) 的关系。
---> 预处理字符串所有前缀Hash值。
---> 在O(1)时间内查询它的任意子串Hash值。
对unsigned long long的使用:
可以用于取模2^64。遇到这种限制条件时就要想到用unsigned long long类型。
可以简洁地声明为typedef unsigned long long ull。因为ull的范围是[0,2^64-1]。
( 2^64:18446744073709551616,即10^19。)
所以,如果ull类型的整数溢出了,就相当于取模2^64了。
而long long的范围是[-2^63,2^63-1],因为有符号的第63位表示“正负”而不表示数值
3.例题与运用
//qwq #include <cmath> #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <algorithm> #include <stack> #include <queue> using namespace std; typedef unsigned long long ull; /*【Oulipo】poj3461 给你两个字符串s1和s2,求出s1在s2中出现的次数。 */ //字符串Hash的模板题 const int b=13331; //base,基数 ull power[1000009]; //存储b^n void init(){ power[0]=1; //b^0=1 for(int i=1;i<1000000;i++) power[i]=power[i-1]*b; //对ull取模 } char s1[10009],s2[1000009]; ull sum[1000009]; //记录主串哈希值的数组 int main(){ init(); int T; cin>>T; while(T--){ scanf("%s%s",s1+1,s2+1); //从第一位开始输入 int n=strlen(s1+1),m=strlen(s2+1); sum[0]=0; //记录主串哈希值的数组 for(int i=1;i<=m;i++) //计算主串的滚动Hash值 sum[i]=sum[i-1]*b+(ull)(s2[i]-‘A‘+1); ull s=0; for(int i=1;i<=n;i++) //计算匹配串的Hash值 s=s*b+(ull)(s1[i]-‘A‘+1); int ans=0; for(int i=0;i<=m-n;i++) if(s==sum[i+n]-sum[i]*power[n]) ans++; printf("%d ",ans); } return 0; }
//qwq #include <cmath> #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <algorithm> #include <stack> #include <queue> using namespace std; typedef long long ll; typedef unsigned long long ull; /*【power strings】poj2406 给出一个不超过1e6的字符串,求这个字符串最多有多少个周期。 */ //字符串Hash的模板题 const int b=131; //base,基数 const int mod=10009; //h,要mod的数 int len; ull hashs[1001000]; ull cal(int x,ull y){ //y^x ull now=1; while(x){ if(x&1) now=(now*y)%mod; x>>=1; y=(y*y)%mod; } return now; } bool check(int x){ //x:最小的循环长度 ll cc=cal(x,(ull)b); for(int i=(x<<1);i<=len;i+=x) if((hashs[i]-(hashs[i-x]*cc)%mod+mod)%mod!=hashs[x]) //求H(C,k+n)-H(C,k)*b^n,之后的每一段长度的hash值是否匹配 //加上mod,防止相减为负值 return false; return true; } int main(){ while(1){ char s[1001000]; scanf("%s",s+1); len=strlen(s+1); if(len==1&&s[1]==‘.‘) return 0; for(int i=1;i<=len;i++) //求出Hash值 hashs[i]=(hashs[i-1]*b+s[i])%mod; for(int i=1;i<=len;i++) //枚举循环的长度 //↓↓↓最小的循环长度对应最大的周期,即第一个满足的就是最大周期数 if(len%i==0 && check(i)){ printf("%d ",len/i); break; } } return 0; }
(3)poj2752
//qwq #include <cmath> #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <algorithm> #include <stack> #include <queue> #include <map> using namespace std; typedef long long ll; typedef unsigned long long ull; /*(bzoj3916)【friends】【hash】 有三个好朋友喜欢在一起玩游戏,A君写下一个字符串S, B君将其复制一遍得到T,C君在T的任意位置(包括首尾)插入一个字符得到U. 现在你得到了U,请你找出S. */ const int P=131; const int N=2000010; ull h[N],t[N]; map<ull,int>q; char ss[N]; int main(){ int n,ans,len; scanf("%d",&n); if(n%2==0||n==1){ puts("NOT POSSIBLE"); return 0; } len=(n-1)/2; scanf("%s",ss+1); t[0]=1;h[0]=0; for(int i=1;i<=n;i++) t[i]=t[i-1]*P; //预处理P^n for(int i=1;i<=n;i++) h[i]=h[i-1]*P+(ull)ss[i]; //hash前缀和 for(int i=1;i<=n;i++){ if(i<=len){ ull l=h[i-1],mid=h[len+1]-t[len+1-i]*h[i],r=h[n]-h[n-len]*t[len]; l=l*t[len-i+1]+mid; if(l==r){ if(q[l]||(!ans)) ans=i,q[l]=1; else{ puts("NOT UNIQUE"); return 0; } } } if(i==len+1){ ull l=h[i-1],r=h[n]-t[len]*h[i]; if (l==r){ if(q[l]||(!ans)) ans=i,q[l]=1; else{ puts("NOT UNIQUE"); return 0; } } } if(i>len+1){ ull l=h[len],mid=h[i-1]-h[len]*t[i-1-len],r=h[n]-h[i]*t[n-i]; r=mid*t[n-i]+r; if(l==r){ if(q[l]||(!ans)) ans=i,q[l]=1; else{ puts("NOT UNIQUE"); return 0; } } } } if(!ans){ puts("NOT POSSIBLE"); return 0; } else{ int t=1; while(len--){ if(t==ans) t=t+1; printf("%c",ss[t]); t++; } } return 0; }
以上是关于暖*墟#Hash# 字符串Hash与例题的主要内容,如果未能解决你的问题,请参考以下文章