hash小结
这个简单且好写
裸的带题面得hash懒得找,找了个板子
给定一个长n的字符串txt和一个长m的字符串str,问txt里能不能找到str;
考虑暴力,O(nm)逐个比对,如果要比对总共k个字符串就是O(nmk),会炸
考虑优化。
#include<bits/stdc++.h>
#define MAXN 10001
#define ull unsigned long long
using namespace std;
ull mod=212370440130137957ll;
const int p=131;
ull has[MAXN];
ull prime=233317;
int n,t,ans=1;
char str[MAXN];
int main(){
scanf("%d",&n);
t=n;
for(int j=1;j<=n;j++){
scanf("%s",str+1);
int l=strlen(str+1);
ull hs=0;
for(int i=1;i<=l;i++)hs=((hs*p)%mod+(ull)str[i]+prime)%mod;
has[j]=hs;
}
sort(has+1,has+n+1);
for(int i=1;i<n;i++)if(has[i]!=has[i+1])ans++;
printf("%d",ans);
return 0;
}
由于这个东西不算一个算法,算是字符串的处理技巧,所以基本上都会辅佐其他算法使用
比如说二分
使用二分答案尝试最长的序列,比对时使用hash快速匹配
#include<bits/stdc++.h>
#define MAXN 501
#define ull unsigned long long
using namespace std;
ull p=13331;
int n,m,ans;
char c1[MAXN][MAXN],c2[MAXN][MAXN];
ull pw[MAXN];
ull has1[MAXN][MAXN],has2[MAXN][MAXN];
map<ull,bool>mp;
bool check(int p){
for(int l=1,r=l+p-1;r<=m;r++,l++){
bool f=1;
mp.clear();
for(int i=1;i<=n;i++)mp[has1[i][r]-has1[i][l-1]*pw[r-l+1]]=1;
for(int i=1;i<=n;i++)if(mp[has2[i][r]-has2[i][l-1]*pw[r-l+1]]){f=0;break;}
if(f)return 1;
}
return 0;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%s",c1[i]+1);
for(int i=1;i<=n;i++)scanf("%s",c2[i]+1);
pw[0]=1;
for(int i=1;i<=MAXN;i++)pw[i]=pw[i-1]*p;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
has1[i][j]=has1[i][j-1]*p+c1[i][j];
has2[i][j]=has2[i][j-1]*p+c2[i][j];
}
}
int l=1,r=m;
while(l<=r){
int mid=l+r>>1;
if(check(mid))ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d",ans);
return 0;
}
或者说DP
推方程即可,例如本题中设dp[i][j]为到i个氨基酸而该放置第k位时的最优解,很容易得到方程。
for(int i=0;i<=lt;i++)dp[0][i]=1,has[i]=has[i-1]*p+txt[i];
for(int t=1,u;t<=k;t++){
scanf("%d",&u);
while(u--){
scanf("%s",str+1);
ls=strlen(str+1);
ull hs=0;
for(int i=1;i<=ls;i++)hs=hs*p+str[i];
for(int l=1,r=l+ls-1;r<=lt;l++,r++){
if(hs==get(l,r))dp[t][r]=(dp[t][r]+dp[t-1][l-1])%mod;
}
}
}
for(int i=1;i<=lt;i++)ans=(ans+dp[k][i])%mod;
printf("%d",ans);
由于不一定要配出完整的序列,最后将dp[k][i]累加即可。
与数据结构结合等。
手写一个栈,如果栈顶ls长度的串的hash与str的hash相等就弹栈,弹到不能再弹为止,每次顺便将txt的栈顶位加入答案,输出即可。教练要求用kmp写不过我太菜就先用hash水AC了
#include<bits/stdc++.h>
#define ull unsigned long long
#define MAXN 1000005
using namespace std;
char txt[MAXN],str[MAXN],ans[MAXN];
int lt,ls;
int p=131;
ull pw[MAXN];
ull hast[MAXN],hass;
ull stk[MAXN];
int top;
int main(){
scanf("%s%s",txt+1,str+1);
lt=strlen(txt+1),ls=strlen(str+1);
pw[0]=1;
for(int i=1;i<=MAXN;i++)pw[i]=pw[i-1]*p;
for(int i=1;i<=ls;i++)hass=hass*p+str[i];
for(int i=1;i<=lt;i++){
ans[++top]=txt[i];
stk[top]=stk[top-1]*p+txt[i];
if(top>=ls&&stk[top]-pw[ls]*stk[top-ls]==hass)top-=ls;
}
for(int i=1;i<=top;i++)printf("%c",ans[i]);
return 0;
}
hash+二分+区间维护
动物园&周期
枚举左端点l,二分右端点r,通过check后更新[l,r]的信息即可。
#include<bits/stdc++.h>//OKRperiod
#define MAXN 1000005//Hash&Binary&SegmentTree
#define ull unsigned long long
#define int long long
using namespace std;
int n,lt,ans;
char txt[MAXN];
ull has[MAXN],pw[MAXN];
const int p=131;
bool check(int l,int r){
return has[r-l+1]==has[r]-has[l-1]*pw[r-l+1]?1:0;
}
struct Segment_Tree{
#define leftson(p) p<<1
#define rightson(p) p<<1|1
#define push_up(p) tree[p].val=max(tree[leftson(p)].val,tree[rightson(p)].val)
struct TREE{
int l,r,val,tag;
}tree[MAXN<<2];
void build(int l,int r,int p){
tree[p].l=l,tree[p].r=r;
if(l==r)return;
int mid=l+r>>1;
build(l,mid,leftson(p));
build(mid+1,r,rightson(p));
}
void spread(int p){
if(tree[p].tag){
tree[leftson(p)].val=max(tree[leftson(p)].val,tree[p].tag);
tree[rightson(p)].val=max(tree[rightson(p)].val,tree[p].tag);
tree[leftson(p)].tag=tree[p].tag;
tree[rightson(p)].tag=tree[p].tag;
tree[p].tag=0;
}
}
void update(int l,int r,int k,int p){
if(tree[p].l>=l&&tree[p].r<=r){
tree[p].val=max(tree[p].val,k);
tree[p].tag=k;
return;
}
spread(p);
int mid=tree[p].l+tree[p].r>>1;
if(l<=mid)update(l,r,k,leftson(p));
if(r>mid)update(l,r,k,rightson(p));
push_up(p);
}
int query(int x,int p){
if(tree[p].l==tree[p].r)return tree[p].val;
spread(p);
int mid=tree[p].l+tree[p].r>>1;
if(x<=mid)return query(x,leftson(p));
else return query(x,rightson(p));
}
}ST;
signed main(){
pw[0]=1;
for(int i=1;i<MAXN;i++)pw[i]=pw[i-1]*p;
scanf("%lld",&n);
scanf("%s",txt+1);lt=strlen(txt+1);
for(int i=1;i<=lt;i++)has[i]=has[i-1]*p+txt[i];
ST.build(1,lt,1);
for(int i=1;i<lt;i++){
int l=i+1,r=min(2*i,lt);
int qans=0;
while(r>=l){
int mid=l+r>>1;
if(check(i+1,mid))qans=mid,l=mid+1;
else r=mid-1;
}
if(qans)ST.update(i+1,qans,i,1)/*,printf("l:%d Qans:%d\n",l,qans)*/;
}
for(int i=2;i<=lt;i++)ans+=ST.query(i,1);
// for(int i=1;i<=n<<2;i++)printf("p:%d l:%d r:%d val:%d\n",i,ST.tree[i].l,ST.tree[i].r,ST.tree[i].val);
printf("%lld",ans);
return 0;
}
#include<bits/stdc++.h>
#define ull unsigned long long//动物园
#define MAXN 1000005//Hash&Binary&diff
#define int long long
using namespace std;
int T,lt,ans;
const int p=131;
const int mod=1e9+7;
ull pw[MAXN],has[MAXN];
char txt[MAXN];
bool check(int l,int r){
if(r-l+1>=l)return 0;
return has[r-l+1]==has[r]-has[l-1]*pw[r-l+1];
}
int d[MAXN],sumd[MAXN];
signed main(){
pw[0]=1;
for(int i=1;i<MAXN;i++)pw[i]=pw[i-1]*p;
scanf("%lld",&T);
while(T--){
ans=1;
memset(d,0,sizeof(d));
scanf("%s",txt+1);lt=strlen(txt+1);
for(int i=1;i<=lt;i++)has[i]=has[i-1]*p+txt[i];
for(int i=1;i<=lt;i++){
int l=i,r=lt,qans=0;
while(r>=l){
int mid=l+r>>1;
if(check(i,mid))qans=mid,l=mid+1;
else r=mid-1;
}
if(qans)d[i]++,d[qans+1]--;
}
for(int i=1;i<=lt;i++)sumd[i]=d[i]+sumd[i-1];
for(int i=1;i<=lt;i++)ans=((ans)*(sumd[i]+1))%mod;
printf("%lld\n",ans);
}
return 0;
}