【 哈希和哈希表】子串查找

【 哈希和哈希表】子串查找

时间限制: 1 Sec  内存限制: 128 MB
提交: 78  解决: 25
[提交] [状态] [讨论版] [命题人:admin]

题目描述

这是一道模板题。
给定一个字符串A和一个字符串B,求B在A中的出现次数。A和B中的字符均为英语大写字母或小写字母。
A中不同位置出现的B可重叠。

输入

输入共两行,分别是字符串A和字符串B。

输出

输出一个整数,表示B在A中的出现次数。

样例输入

zyzyzyz
zyz

样例输出

3

提示

1≤A,B的长度≤106,A、B仅包含大小写字母。

 

一开始用kmp做的,后来知道了hash,突然发现了一个新世界

 

就是将一个字符串映射到一个整数,将整数代替字符串,

显然会出现几个不同的字符串映射到同一个整数,这种情况是要避免的,不然你就wa了

显然这些数越分散,

或者说,嗯,把熵这个概念引进来比较好理解,

熵越大(越杂乱),

这样就越不会出现几个不同的字符串映射到同一个整数上的情况

 

用的双hash,一个hash,wa了

 

一般k,p不要用1e9+7 或者1e9+9,出题人可能会卡,这个题用了双hash,

所以1e9+7,1e9+9没问题,但是别的题不一定不会卡,嗯,还是避免的好

 

一般的hash

 

hash[i]=(hash[i-1]*k+id(a[i]) )mod(p)

 

子串求hash公式

 

hash=((hash[r]-hash[l-1]*k^{r-l+1})mod(p)+p)mod(p)

 

双hash就是hash两遍,

,表示一个字符串,注意这个有顺序的

即, 不等于 

 

放一个双hash的板子,嗯,题目是洛谷P3370

板子换掉,去掉了取模操作,改用unsigned ll

,嗯,

改之前不开o2, 2534ms /  1.28MB ,开o2, 729ms /  1.3MB 

改之后不开o2, 1295ms /  1.27MB ,开o2, 139ms /  1.27MB 

速度确实上来了,尬,忘记之前用的cin了,改成scanf开o2 382ms /  1.5MB 

也可以了,200ms的提升

#include 
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair pull;
const int maxn = 1e6+7;
map M;
const pull p{131LL,13331LL},one{1LL,1LL},zero{0LL,0LL};
pull operator - (pull a,pull b){return make_pair((a.first -  b.first),(a.second - b.second));}
pull operator * (pull a,pull b){return make_pair((a.first *  b.first),(a.second * b.second));}
pull operator + (pull a,pull b){return make_pair((a.first +  b.first),(a.second + b.second));}
pull operator + (pull a,int  b){return make_pair((a.first +  b      ),(a.second + b       ));}
pull Pow(pull a,int n){
    pull ans = one;
    while(n){
        if(n&1)
            ans = ans*a;
        a = a*a;
        n>>=1;
    }
    return ans;
}
pull Hash(char a[]){
    pull ans = zero;
    for(int i=0;a[i];i++)
        ans = ans*p + a[i];
    return ans;
}
char a[maxn];
int main(){
    int n,cnt = 0;
    scanf("%d",&n);
    while(n--){
        scanf("%s",a);
        pull x = Hash(a);
        if(!M.count(x))
            cnt++,M[x] = 1;
    }
    printf("%d\n",cnt);
    return 0;
}

 

 

#include 
using namespace std;
typedef long long ll;
typedef pair pll;
map M;
const int maxn = 1e6+7;
const pll k{1e9+9,1e9+7},p{1e9+7,1e9+9},one{1ll,1ll},zero{0ll,0ll};
pll operator - (pll a,pll b){return make_pair((a.first - b.first + p.first)%p.first,(a.second - b.second + p.second)%p.second);}
pll operator * (pll a,pll b){return make_pair((a.first * b.first)%p.first,(a.second * b.second)%p.second);}
pll operator + (pll a,pll b){return make_pair((a.first + b.first)%p.first,(a.second + b.second)%p.second);}
pll operator + (pll a,int b){return make_pair((a.first + b)%p.first,(a.second + b)%p.second);}
pll Hash(char a[],int len){
    pll ans = zero;
    for(int i=0;i>n;
    while(n--){
        cin>>a;
        pll x = Hash(a,strlen(a));
        if(!M.count(x))
            cnt++,M[x] = 1;
    }
    cout<

 

#include 
using namespace std;
typedef long long ll;
typedef pair pll;
const int maxn = 1e6+7;
const ll k = 998244353, p = 1e9+7;
const ll s = 1e9+7, q = 1e9+9;
char a[maxn],b[maxn];
pll Hash(char a[],int len){
    pll ans{0ll,0ll};
    for(int i=0;i>a>>b;
    int lb = strlen(b);
    pll x = Hash(b,lb);
    pll r = Hash(a,lb);
    pll l = {0ll,0ll};
    pll c = {1ll,1ll};
    for(int i=0;i

 

 

精简精简,搜了下c++重载操作,嗯,缩减代码的效果很强,就是写的时候

看着这一串重载头有点晕

下面有带注释的代码

纯代码

#include 
using namespace std;
typedef long long ll;
typedef pair pll;
const int maxn = 1e6+7;
const ll k = 1e9+9, p = 1e9+7;
const ll s = 1e9+7, q = 1e9+9;
pll Tc{k,s};
pll operator - (pll a,pll b){return make_pair((a.first - b.first + p)%p,(a.second - b.second + q)%q);}
pll operator * (pll a,pll b){return make_pair((a.first * b.first)%p,(a.second * b.second)%q);}
pll operator + (pll a,pll b){return make_pair((a.first + b.first)%p,(a.second + b.second)%q);}
pll operator + (pll a,int b){return make_pair((a.first + b)%p,(a.second + b)%q);}
pll init(int n){
    pll ans{1ll,1ll};
    for(int i=0;i>a>>b;
    int lb = strlen(b);
    pll x = Hash(b,lb), r = Hash(a,lb), l{0ll,0ll}, k_lb = init(lb);
    ll cnt = 0;
    for(int i=lb ; a[i] ; r = r*Tc + a[i],l = l*Tc + a[i-lb],i++)
        if(r - (l*k_lb) == x)
            cnt++;
    if(r - (l*k_lb) == x)cnt++;
    cout<

 

注释代码

#include 
using namespace std;
typedef long long ll;
typedef pair pll;
const int maxn = 1e6+7;
const ll k = 1e9+9, p = 1e9+7;
const ll s = 1e9+7, q = 1e9+9;
pll Tc{k,s};///定义一个乘数,相当于单hash里的K
pll operator - (pll a,pll b){return make_pair((a.first - b.first + p)%p,(a.second - b.second + q)%q);}
///重载的时候注意到可能出现负数
pll operator * (pll a,pll b){return make_pair((a.first * b.first)%p,(a.second * b.second)%q);}
///注意取模
pll operator + (pll a,pll b){return make_pair((a.first + b.first)%p,(a.second + b.second)%q);}
pll operator + (pll a,int b){return make_pair((a.first + b)%p,(a.second + b)%q);}
///这个也可以不用,就是在加的时候注意用make_pair(b,b)代替b,然后用上面的加法就可以了
pll init(int n){///初始化那个求子串的时候 的 k的r-l+1次方
    pll ans{1ll,1ll};
    for(int i=0;i>a>>b;
    int lb = strlen(b);
    pll x = Hash(b,lb)///b串hash成的值
    , r = Hash(a,lb)///区间右端点
    , l{0ll,0ll}///区间左端点
    , k_lb = init(lb);///k 的 r-l+1 (lb)次方
    ll cnt = 0;
    for(int i=lb ; a[i] ; r = r*Tc + a[i],l = l*Tc + a[i-lb],i++)///r,l相应的向后乘
        if(r - (l*k_lb) == x)///如果区间等于b的hash值
            cnt++;
    if(r - (l*k_lb) == x)cnt++;///因为for少一个区间
    cout<

 

你可能感兴趣的:(模板类(什么,这也是模板))