时间限制: 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公式
双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<