admin 管理员组

文章数量: 887021

hash,kmp题组所有题解

目录

【模板】字符串哈希

【模板】KMP字符串匹配

于是他错误的点名开始了

A-B 数对

Barn Echoes G

Compress Words


【模板】字符串哈希

- [P3370 【模板】字符串哈希](【模板】字符串哈希 - 洛谷)

题解:对每个字符串进制哈希,并将哈希值存到ans数组里,统计里面不一样的值的个数,输出即可。

代码实现如下:

#include<bits/stdc++.h>
using namespace std;
char s[1501];
long long ans[10001],hashe[10001][1501];
int main()
{int n,base=131,number=1,len=0;long long mod=1e9+7;scanf("%d",&n);for(int i=1; i<=n; i++){scanf("%s",s);len=strlen(s);//进制哈希for(int j=0; j<len; j++){if(j==0)hashe[i][j]=s[j];else hashe[i][j]=(hashe[i][j-1]*base+s[j])%mod;}ans[i]=hashe[i][len-1];}sort(ans+1,ans+n+1);//统计数组ans中不相同的数字个数for(int i=1;i<n;i++){if(ans[i]!=ans[i+1])number++;}printf("%d",number);return 0;
}

【模板】KMP字符串匹配

- [P3375 【模板】KMP字符串匹配](【模板】KMP字符串匹配 - 洛谷)

题解:这题就是一道KMP的模板题,输出匹配到的出现的所有位置,最后一行直接输出next数组即可

#include<stdio.h>
#include<string.h>
char s1[1000100],s2[1000100];
int next[1000100];
void Next()
{int n=strlen(s2+1);int j=0;for(int i=2;i<=n;i++){while(j&&s2[j+1]!=s2[i])j=next[j];if(s2[j+1]==s2[i])j++;next[i]=j;}
}
void KMP()
{Next();//根据模式串T,初始化next数组int m=strlen(s1+1),n=strlen(s2+1);int j=0;for(int i=1;i<=m;i++){while(j&&s2[j+1]!=s1[i])j=next[j];if(s2[j+1]==s1[i])j++;if(j==n){printf("%d\n",i-n+1);j=next[j];}}
}
int main()
{scanf("%s",s1+1);scanf("%s",s2+1);KMP();for(int i=1;i<=strlen(s2+1);i++){printf("%d ",next[i]);}return 0;
}

于是他错误的点名开始了

- [P2580 于是他错误的点名开始了](于是他错误的点名开始了 - 洛谷)

在思考了很久后,我也没有想出啥好的方法,于是看了下题解中说用map容器非常简单,后来了解到这是STL库里的内容,随后我又花时间去学了一下STL库里的string,set,map。vector暂时还没学,但解决这题也足够了。

题解:利用STL中的map容器将班上的人的名字作为键值,标记为1,然后扫一遍,如果为1,输出OK,同时将1更新为2,如果遇到大于1的,输出REPEAT,如果为0,说明没有,输出WRONG。

#include<bits/stdc++.h>
using namespace std;
string s;
map<string,int>mp;
int main()
{int n=0;scanf("%d",&n);for(int i=1; i<=n; i++){cin>>s;//不能用scanf("%s")mp[s]++;s.clear();}int m=0;scanf("%d",&m);for(int i=1; i<=m; i++){cin>>s;if(mp[s]==0)printf("WRONG\n");if(mp[s]==1){printf("OK\n");mp[s]++;}if(mp[s]>1)printf("REPEAT\n");s.clear();}return 0;
}

A-B 数对

- [P1102 A-B 数对](A-B 数对 - 洛谷)

题解:假设他们可能有A,B双重身份。再把每个数(相当于是B)都加上C,扫一遍,看看有没有相等的A与之对应。

#include<bits/stdc++.h>
using namespace std;
int a[200001];
map<int,int>mp;
int main()
{int n=0,c=0;long long ans=0;scanf("%d%d",&n,&c);for(int i=1;i<=n;i++){scanf("%d",&a[i]);mp[a[i]]++;}for(int i=1;i<=n;i++){ans+=mp[a[i]+c];//反向思维,枚举 B+C}printf("%lld",ans);return 0;
}

事实上在这个过程中我并没有那么顺利,因为刚开始我做的时候没有考虑到“不同位置的数字一样的数对算不同的数对”,所以我在映射时使mp[a[i]]=1,而不是mp[a[i]]++。

Barn Echoes G

- [P2957 [USACO09OCT]Barn Echoes G]([USACO09OCT]Barn Echoes G - 洛谷)

题解:利用string的内置函数substr,截取第一个字符串的前缀和第二个字符串的后缀,取最大相等长度i,截取第二个字符串的前缀和第一个字符串的后缀,取最大相等长度j。在i、j中取最大值即可。

#include<bits/stdc++.h>
using namespace std;
string s1,s2;
int main()
{cin>>s1;cin>>s2;int i,j;int l1=s1.size();int l2=s2.size();int minlen=min(l1,l2);for(i=minlen;i>0;i--){if(s1.substr(0,i)==s2.substr(l2-i,i))//截取的也是string,可直接用==判定,string从0开始存,所以是l2-ibreak;}for(j=minlen;j>0;j--){if(s2.substr(0,j)==s1.substr(l1-j,j))break;}int ans=max(i,j);printf("%d",ans);return 0;
}

Compress Words

- [CF1200E Compress Words](Compress Words - 洛谷)

题解:这个题目就是找第一个串的后缀和第二个串的前缀拼在一起,找到他们最长的公共部分拼在一起。这很容易让人联想到KMP中的next数组。那么让谁来作为模式串呢?我们可以把新加入的串放在前面,现有的串放在后面。特别注意的是最长公共前后缀的长度实际上只要到min(字符串1的长度,字符串2的长度)就行了,所以当现有长度大于新加入字符串长度(若新加入字符串长度为L)时,只要在现有字符串后面截取L长度接在新加入字符串后面就构成了模式串,还需要注意的是两个字符串中间需要加入一些特别的字符,以防最长公共前后缀超过待拼接串的长度。例如:

1001101       010

最后将101和010合并后KMP时出现了问题,合并后的字符串:010101

最长公共前后缀长度:4>待拼接串的长度3

最终代码实现如下:

#include<bits/stdc++.h>
using namespace std;
int nxt[1000001];//不能写next,next是关键字,会被认为有歧义
void Next(string s)//最长公共前后缀中前缀的最后一位位置
{int l=s.size();s=' '+s;//前面加个空格,让s下标从1开始int j=0;for(int i=2; i<=l; i++){while(s[j+1]!=s[i]&&j)j=nxt[j];if(s[j+1]==s[i])j++;nxt[i]=j;}}
int main()
{ios::sync_with_stdio(false);//让cin变得快一点int n;cin>>n;string s,ans;//从0开始存cin>>ans;for(int i=2; i<=n; i++){cin>>s;int l=min(s.size(),ans.size());string ss=s+'#'+ans.substr(ans.size()-l,l);//把两个字符串隔开Next(ss);for(int j=nxt[ss.size()]; j<s.size(); j++) //实际上j=nxt[ss.size()-1+1],因为s从0开始存ans+=s[j];}cout<<ans;return 0;
}

本文标签: hash kmp题组所有题解