典型例题:给出两个字符串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; }
这道题可以直接用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; }
给定若干个长度 ≤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; } } } } }
给定若干字符串(这些字符串总长 ≤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; }
原文:https://www.cnblogs.com/shirlybaby/p/12718694.html