2022.02.09学习总结

学习了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

刷题 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值