学习了kmp和hash算法,了解了两种算法的作用和优点,掌握其中概念,且进行了代码实现。感觉学习的过程中还是有点吃力😥
题目描述
如题,给定 NN 个字符串(第 ii 个字符串长度为 M_iMi,字符串内包含数字、大小写字母,大小写敏感),请求出 NN 个字符串中共有多少个不同的字符串。
友情提醒:如果真的想好好练习哈希的话,请自觉,否则请右转PJ试炼场:)
输入格式
第一行包含一个整数 NN,为字符串的个数。
接下来 NN 行每行包含一个字符串,为所提供的字符串。
输出格式
输出包含一行,包含一个整数,为不同的字符串个数。
输入输出样例
输入 #1复制
5 abc aaaa abc abcc 12345输出 #1复制
4说明/提示
对于 30\%30% 的数据:N\leq 10N≤10,M_i≈6Mi≈6,Mmax\leq 15Mmax≤15。
对于 70\%70% 的数据:N\leq 1000N≤1000,M_i≈100Mi≈100,Mmax\leq 150Mmax≤150。
对于 100\%100% 的数据:N\leq 10000N≤10000,M_i≈1000Mi≈1000,Mmax\leq 1500Mmax≤1500。
#include<bits/stdc++.h>
#include<string>
#include<cmath>
using namespace std;
typedef unsigned long long ull;
ull x=1331,num[10001];//x为进制,当取进制为131,1331,13331时...冲突最小,num数组为字符串hash后所装的数据
ull mod=pow(2,62);//取模,当模越大时,可降低hash冲突
string str;//需要hash的字符串
ull jzhash(string S)
{
ull sum=0;
for(int i=1;i<=S.length();i++){//计算每个字符串对应的hash数据
sum=(sum*x+(ull)S[i])%mod;
} return sum;
}
int main()
{
int n,sum=1;
scanf("%d",&n);
getchar();
for(int i=1;i<=n;i++){
cin>>str;
//scanf("%s",str);
getchar();
num[i]=jzhash(str);
}
sort(num+1,num+n+1);//将数据进行排序,以便查询相同数据
for(int i=1;i<n;i++){
if(num[i]!=num[i+1]){
sum++;//若不同,则总数加一
}
}
cout<<sum;//输出
}
kmp模板
题目描述
给出两个字符串 s_1s1 和 s_2s2,若 s_1s1 的区间 [l, r][l,r] 子串与 s_2s2 完全相同,则称 s_2s2 在 s_1s1 中出现了,其出现位置为 ll。
现在请你求出 s_2s2 在 s_1s1 中所有出现的位置。定义一个字符串 ss 的 border 为 ss 的一个非 ss 本身的子串 tt,满足 tt 既是 ss 的前缀,又是 ss 的后缀。
对于 s_2s2,你还需要求出对于其每个前缀 s's′ 的最长 border t't′ 的长度。输入格式
第一行为一个字符串,即为 s_1s1。
第二行为一个字符串,即为 s_2s2。输出格式
首先输出若干行,每行一个整数,按从小到大的顺序输出 s_2s2 在 s_1s1 中出现的位置。
最后一行输出 |s_2|∣s2∣ 个整数,第 ii 个整数表示 s_2s2 的长度为 ii 的前缀的最长 border 长度。输入输出样例
输入 #1复制
ABABABC ABA输出 #1复制
1 3 0 0 1
#include<bits/stdc++.h>
#include<string>
using namespace std;
int N[1000001];
void GetNext(string T)//推导子串的N数组,提高查找速度
{
int i,k;
i=0,k=-1;
N[0]=-1;//当下标为0时,值为-1
while(i<=T.length()-1){//遍历整个子串
if(k==-1||T[i]==T[k]){//寻找最大相同前后缀
++i;++k;
N[i]=k;
}
else{
k=N[k];//若字符不相等,则回溯k值
}
}
}
void KMP(string S,string T)//主串,子串
{
int i=0;int j=0;//i主串当前位置下标值,j为子串当前位置下标值
GetNext(T);
while(i<S.length()){
while(j&&S[i]!=T[j]){
j=N[j];//若不匹配,j退回合适位置
}if(S[i]==T[j]) j++;//匹配则移动j
if(j==T.length()){
printf("%d\n",i-T.length()+2);//输出每次出现子串的位置
j=N[j];//退回
}
i++;
}}
int main()
{
string T,S;
cin>>S;
getchar();//cin输入会包含换行
cin>>T;
KMP(S,T);
for(int i=1;i<=T.length();i++){
cout<<N[i]<<" ";
}
}
明日计划
继续学习kmp和hash
刷题