首页 > 其他 > 详细

字符串hash例题(一本通)

时间:2020-04-17 12:51:31      阅读:165      评论:0      收藏:0      [点我收藏+]

1455:【例题1】Oulipo

典型例题:给出两个字符串s1,s2,求s1在s2中出现多少次

ps.求子串的hash公式

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1e6+10;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
//字符串hash模板题
char s1[maxn],s2[maxn];
ull h[maxn];
ull power[maxn];  //每一步要乘的
ull b=27,mod=1<<31;
//只有大写字母 
int main(){
	power[0]=1;
	for(int i=1;i<=1000000;i++) power[i]=power[i-1]*b;
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%s",s1+1);
		scanf("%s",s2+1);
		int n=strlen(s1+1);
		int m=strlen(s2+1);
		h[0]=0;
		ull ans=0,s=0;
		for(int i=1;i<=m;i++){  //算出s2的hash值  每一位 
			h[i]=(h[i-1]*b+(ull)(s2[i]-‘A‘+1))%mod;
		}
		for(int i=1;i<=n;i++) s=(s*b+(ull)(s1[i]-‘A‘+1))%mod;   //s1的hash值 
		for(int i=0;i<=m-n;i++){
			if(s==h[i+n]-h[i]*power[n]) ans++;
		} 
		printf("%d\n",ans);
	}
return 0;
}

1456:【例题2】图书管理

技术分享图片

 

 

 这道题可以直接用map来做,map<ull,int> a;

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=30010;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull; 
int n;
int mod1=100005;
int k1=233317;
map<ull,int> a; //用map来做 
char op[10],ss[210];
int main(){
	scanf("%d",&n);
	//int num=0;
	while(n--){
		scanf("%s ",op);
		gets(ss);
		ull num=0;
		for(int i=0;i<strlen(ss);i++) 
		num=(num*k1+(int)ss[i]);
		if(op[0]==‘f‘){
			if(a[num]==1) printf("yes\n");
			else printf("no\n");
		} 
		else a[num]=1;
	}
return 0;
}

1457:Power Strings

给定若干个长度 ≤106 的字符串,询问每个字符串最多是由多少个相同的子字符串重复连接而成的。如:ababab 则最多有 3 个 ab 连接而成。

abcd   1
aaaa   4
ababab  3
.

询问每个字符串最多是由多少个相同的子字符串重复连接而成的 求最短重复子序列
这道题试问我们一个串有几个循环节。循环节就是指相等的(真)前缀和(真)后缀的个数。我们知道,
kmp过程中的next[i]是这个意义:0-i-1位中相等的真前后缀个数。那么next[len]就是指0-len-1位中相等的真前后缀个数。

所以第一种做法就是,求next数组

if(len%(len-next[len])==0) printf("%d\n",len/(len-next[len]));

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1e6+10;
const int INF=0x3fffffff;
typedef long long LL;
//询问每个字符串最多是由多少个相同的子字符串重复连接而成的  求最短重复子序列
//这道题试问我们一个串有几个循环节。循环节就是指相等的(真)前缀和(真)后缀的个数。我们知道,
//kmp过程中的next[i]是这个意义:0-i-1位中相等的真前后缀个数。那么next[len]就是指0-len-1位中相等的真前后缀个数。
char s[maxn];
int next[maxn],len;
void getnext(){
	int j=-1;
	int i=0;
	next[0]=-1;
	while(i<len){
		if(j==-1||s[i]==s[j]){
			i++;j++;
			next[i]=j;
		}
		else j=next[j];
	}
}
int main(){
	while(1){
		scanf("%s",s);
		if(s[0]==‘.‘) break;
		memset(next,0,sizeof(next));
		len=strlen(s);
		getnext();
		if(len%(len-next[len])==0) printf("%d\n",len/(len-next[len]));
		else printf("1\n");
	}
return 0;
}

  第二种做法:普通做法,hash,判断连续k个字母的hash值是不是都是一样的

//普通做法
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn = 1e6+10;
typedef unsigned long long ull;
char s1[maxn];
ull power[maxn],h[maxn];
ull n,s,base=131,mod=1<<31;
int check(ull v,int k){
	for( ull i=0; i<n; i+=k ){
		if(h[i+k]-h[i]*power[k]!=v) return 0;  //计算字串的hash值 
	}
	return 1;
}
int main(){
	power[0]=1;
	for( int i=1; i<=101000; i++ ) power[i]=power[i-1]*base;
	while(scanf("%s",s1+1)){
		if(s1[1]==‘.‘) break;
		n=strlen(s1+1);
		h[0]=0;
		for( int i=1; i<=n; i++ ) h[i]=h[i-1]*base+(ull)(s1[i]-‘A‘+1);
		for( int i=1; i<=n; i++ ){
			if(n%i==0){
				if(check(h[i],i)==1){
					printf("%d\n",n/i);break;
				}
			}
		}
	}
}

  

1458:Seek the Name, Seek the Fame

给定若干字符串(这些字符串总长 ≤4×105?? ),在每个字符串中求出所有既是前缀又是后缀的子串长度。

例如:ababcababababcabab,既是前缀又是后缀的:ab,abab,ababcabab,ababcababababcabab。

前后缀:肯定就想到了next数组

这道题不需要重复求出next数组,只需要不断向前移动next

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=4e5+10;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
//又是next数组? 但是next数组是求不相交的
char s[maxn];
int next[maxn];
int len=0;
void getnext(){
	int i=0,j=-1;
	next[0]=-1;
	while(i<len){
		if(j==-1||s[i]==s[j]) {
			i++;j++;
			next[i]=j;
		}
		else j=next[j];
	}
}
int op[maxn];
int main(){
	while(~scanf("%s",s)){
		memset(next,0,sizeof(next));
		len=strlen(s);
		getnext();
		int num=0;
		memset(op,0,sizeof(op));
		
	
		//不用循环做next数组,直接取呀!!! 
		for(int i=len;next[i]!=-1;){
			op[++num]=i; //len也写进来了 
			i=next[i];
		}
		for(int i=num;i>=1;i--) printf("%d ",op[i]);
		printf("\n"); 
	}
	
return 0;
}

  

字符串hash例题(一本通)

原文:https://www.cnblogs.com/shirlybaby/p/12718694.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!