KMP非常不好理解,建议在网上搜KMP的视频看看,反正非常难理解,我想了好久好久(KMP的关键就在于求Next数组D,求前缀后缀)
下面的D题,B题稍稍有变化 ,C题只用求前缀后缀就只用求next数组
标准求next数组做法,
int len=moshi.length();
n[0]=-1;
int k=-1;
int j=0;
while(j<len-1){
if(k==-1||moshi[j]==moshi[k]){
++k;
++j;
n[j]=k;
}else{
k=n[k];
}
}
题目描述
如题,给出n个字符串,让你输出其中共有多少个不同的字符串。请使用字符串哈希解决本题。
输入
第一行一个正整数n表示有n个字符串。
接下来n行每行一个字符串,每个字符串可以由数字和大小写字母组成,大小写敏感。
1 ≤ n ≤ 1000,每个字符串最大长度为1500
输出
输出一行包含一个整数,表示其中不同字符串的个数。
样例输入
5
abc
aaaa
abc
abcc
12345
样例输出
4
本题用哈希的方法做:一个字符串对应一个哈希值,然后把这个哈希值对应到一个数组的坐标上。
#include<bits/stdc++.h>
using namespace std;
const int maxn=10000+5;
#define p 13
#define mod 101
set<string> ss;
int a[maxn];
int hashh(string s){
int len=s.length();
int sum=0; //哈希值
for(int i=0;i<len;i++){
sum+=(s[i]*p)%mod;
}
if(a[sum]==0){ //对应到数组左标上 为0说明以前没加过,然后赋值为一,返回值为1
a[sum]=1;
return 1;
}
else
return 0 //否则说明这个字符串以前加过;返回零
}
int main(){
string s;
int n;
while(cin>>n){
memset(a,0,sizeof(a));
for(int i=0;i<n;i++){
if(hashh(s)) //为1时说明以前没加过,然后加到set里(用vector也行)
ss.insert(s);
}
cout<<ss.size()<<endl; //然后直接用set的大小值输出
}
}
//下面这个为标准做法
#include <cstdio>
#include <iostream>
#include <map>
#include <set>
#include <cstring>
using namespace std;
const int maxn = 1507;
const int p = 13;
const int mod = 101;
int Hash[maxn];
int main()
{
int n;
cin >> n;
set<int> ss;
while(n--){
string tmp;
cin >> tmp;
int i;
for(i = 0; i < tmp.size(); i++){
if(i == 0) Hash[i] = int(tmp[i] - 'a');
else Hash[i] = (Hash[i-1] * p + int(tmp[i] - 'a' + 1)) % mod;
}
ss.insert(Hash[i-1]);
memset(Hash, 0, sizeof Hash);
}
cout << ss.size() << endl;
return 0;
}
题目描述
赵神最近在做一份兼职——帮人查一串字符串中某个单词的出现次数。
当工作太多时,赵神就忙不过来啦,所以他想找一名助手帮他。
输入
第一行输入一个 T ,代表数据数目。
每组数据第一行输入需要查找的单词。
第二行输入这个较长的字符串(长度小于1000010)。
输出
每组数据输出一个 n ,代表要这个单词出现的次数。
样例输入
3
ABCD
ABCD
AZA
AZAZAZA
HIDSJ
FJOSJWHABNMDS
样例输出
1
3
0
kmp算法做,但是也不完全和kmp一样,因为第二种情况按kmp是应该输出2的,但是他是3,是因为他是统计这个出现次数
相当于j走完了i并没有j加一位 ,而是直接在这一位上继续比较,
那这个情况就变成next数组多求一位
AZA AZAx x是空格,他肯定是不匹配的,他会移动到第二个位置
j j AZAX
AZAZAZA AZAZAZA AZAZAZA
i i
#include<bits/stdc++.h>
using namespace std;
string moshi,mubiao;
const int maxn=1000010;
int n[maxn];
void init(){ //求next数组 根据next[j]=k(j位置发生失配时,回到位置k)求next[j+1]等于多少
memset(n,0,sizeof(n));
int len=moshi.length();
n[0]=-1; //next数组的第一个赋值为-1
int k=-1; //第一次k赋值为1
int j=0; //下标从0开始
while(j<len){ //kmp本来是小于len-1,多求一位就变成小于len
if(k==-1||moshi[j]==moshi[k]){ //k=-1说明此时回到的是头部 moshi[j]=moshi[k]说明有后缀和前缀
++k; //此时当前k+1;
++j; //j的位置走加1
n[j]=k; //将K的值赋值给n[j];
}else{ //否则k的值变为回到位置上的值
k=n[k];
}
}
}
int kmp(){
int lenmoshi=moshi.length(),lenmubiao=mubiao.length();
int i=0,j=0,sum=0;
while(i<lenmubiao){ //走目标串
if(mubiao[i]==moshi[j]){ //如果目标串的对应元素和模式串的对应元素相等
i++;
j++;
}else if(j>=0){//回到位置是值是大于等于0的
j=n[j]; //失配时,模式串的位置变到n数组位置上的值 失配时,模式串移到哪 失配时,先走模式串先走回到哪,在进行匹配
}else{ //代表j=-1了说明第一个就发生了失配 目标串的位置+1
++i;
++j;//j+1变为0 下一次进入循环就变为mubiao[i]是否等于moshi[0]
}
if(j==lenmoshi){ //走完了模式串
sum++;
}
}
return sum;
}
int main(){
int n;
cin>>n;
while(n--){
cin>>moshi>>mubiao;
init();
cout<<kmp()<<endl;
}
}
题目描述
小林最近开始研究算命。
虽然他不会算命,但是他可以自己创造一个算命的算法
当一个人的名字的前缀,与另一个人的名字后缀,相同的字母越多,这两个人就越有可能成为情侣。(此处前后缀可以包括名字本身)。
这个算姻缘的方法使得许多妹子前来测试。然而,由于他最近忙着写线段树,没空帮人算姻缘啦。
这里还有许多没测完呢。你能帮帮美丽的妹子们算算吗?
输入
输入有多组数据。
每组数据有两行,分别为两个人的名字(名字全由英文组成,名字长度不超过50000)。
输出
求出前一个人的名字的前缀,与后一个人的名字的后缀,最大的相同数目。
若不为0,还需输出其相同的几位字母,并且字母在数字前面,中间由空格隔开。
样例输入
mike
aniom
kiava
dvakia
dasds
fdsgh
样例输出
m 1
kia 3
0
#include<bits/stdc++.h>
using namespace std;
const int maxn=50000+5;
int Next[maxn];
string s1,s2;
void inint_next(string s){
memset(Next,0,sizeof(Next));
}
int main(){
string s1,s2;
while(cin>>s1>>s2){
string s=s1+s2;
// cout<
int len=s.length();
int len1=s1.length();
int j=0,k=-1;
Next[0]=-1;
while(j<len){
if((k==-1||s[k]==s[j])&&k<len1){
++k;
++j;
Next[j]=k;
}else{
k=Next[k];
}
}
//for(int i=0;i
//cout<
// }
// cout<
int cnt=Next[len];
for(int i=0;i<cnt;i++){
cout<<s[i];
}
if(cnt!=0) cout<<" ";
cout<<cnt<<endl;
}
}
题目描述
左左有N串长度比较短的牛肉串,俊俊有一串长度比较长的牛肉串。牛肉串都是由字符组成,俊俊想知道左左的每一串牛肉串在自己的牛肉串中出现的次数。
输入
输入多组数据。
输入一个数字N代表左左的N串牛肉串。1 <= N <= 10。
接下来N行为左左的牛肉串中的字符。字符串长度不超过10。
第N+1行,俊俊的牛肉串中的字符。字符串长度不超过100。
输出
输出一个数字代表左左的N串牛肉串在俊俊的牛肉串中出现的次数。
样例输入
5
she
he
say
shr
her
yasherhs
样例输出
3
kmp算法
#include<bits/stdc++.h>
using namespace std;
string mubiao;
const int maxn=1000010;
int n[maxn];
vector<string> v;
void initnext(string moshi){
memset(n,0,sizeof(n));
int len=moshi.length();
n[0]=-1;
int k=-1;
int j=0;
while(j<len-1){
if(k==-1||moshi[j]==moshi[k]){
++k;
++j;
n[j]=k;
}else{
k=n[k];
}
}
}
int kmp(string moshi){
int lenmoshi=moshi.length(),lenmubiao=mubiao.length();
int i=0,j=0,sum=0;
while(i<lenmubiao-1){
if(j==-1||mubiao[i]==moshi[j]){
i++;
j++;
}else{
j=n[j];
}
if(j==lenmoshi)
sum++;
}
return sum;
}
int main(){
int n;
cin>>n;
int ans=0;
string moshi;
while(n--){
cin>>moshi;
v.push_back(moshi);
}
cin>>mubiao;
vector<string> ::iterator it;
for(it=v.begin();it!=v.end();it++){
initnext(*it);
ans+=kmp(*it);
}
cout<<ans<<endl;
}
题目描述
红红的棚子里养着一群兔子。
有一天,红红想要研究兔子的 DNA 序列和兔子的长相。
首先选取一个好长好长的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 个小写英文字母)。
然后每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子是否一模一样。
注意两个兔子一模一样只可能是他们的 DNA 序列一模一样。
请使用字符串hash
输入
第一行输入一个 DNA 字符串 S。
第二行一个数字 m,表示 m 次询问。
1 ≤length(S)m≤1000000
接下来 m 行,每行四个数字 L1 R1 L2 R2,分别表示此次询问的两个区间,注意字符串的位置从1开始编号。
输出
对于每次询问,输出一行表示结果。
如果两只兔子完全相同输出 Yes,否则输出 No(注意大小写)。
样例输入
aabbaabb
3
1 3 5 7
1 3 6 8
1 2 1 2
样例输出
Yes
No
Yes
//哈希做法
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1000015;
const int p = 1e5+3;
//一般如果数据比较多比较大的话,p可以取一个六位数的素数,mod取1e9+7这样子
const int mod = 1e9+7;
char ch[maxn];
LL h[maxn], pi[maxn];
LL get(int l, int r) //求一段区间的哈希值
{
return ((h[r] - h[l - 1] * pi[r - l + 1]) % mod + mod) % mod;
}
int main()
{
scanf(" %s", ch + 1);
memset(h, 0, sizeof h);
int n = strlen(ch + 1); //从ch[1]开始存
pi[0] = 1;
for (int i = 1; i <= n; i++) {
h[i] = (h[i - 1] * p + ch[i] - 'a' + 1) % mod; //利用前缀和的思想求哈希值
pi[i] = (pi[i - 1] * p) % mod;
}
int m;
cin >> m;
while (m--) {
int l, r, x, y;
cin >> l >> r >> x >> y;
if (get(l, r) == get(x, y))
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}
//利用截取字符串方法做
#include<bits/stdc++.h>
using namespace std;
int main(){
string s;
cin>>s;
int n;
cin>>n;
while(n--){
int abegin,aend,bbegin,bend;
cin>>abegin>>aend>>bbegin>>bend;
string s1,s2;
s1=s.substr(abegin-1,aend-abegin+1);
s2=s.substr(bbegin-1,bend-bbegin+1);
// for(int i=abegin-1;i
// s1+=s[i];
// for(int j=bbegin-1;j
// s2+=s[j];
if(s1==s2)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
}