合并序列&&kmp算法的初见与实现

本文深入探讨了C++中字符串操作函数find、find_first_of、find_last_of等的使用方法,通过实例展示了如何利用这些函数解决实际问题。同时,文章还介绍了KMP算法的原理及实现,对比find函数,强调了KMP算法在处理大规模数据和重复字符串搜索时的效率优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这题有一个很巧妙的方法,使用find函数 这里就浅谈一下find函数的用法:
1.find()

查找第一次出现的目标字符串:
a.find(b)就是从a中寻找b子串,如果找到的话就返回第一次找到他的下标,如果没找到的话就返回一个
负数值 举个例子:

#include<bits/stdc++.h>
using namespace std;
int main() {
  ios::sync_with_stdio(false);
  cin.tie(false);
  cout.tie(false);
  string a,b;
  a="1234567";
  b="4567";
  int c=a.find(b);
  cout<<c;
  string d="13";
  int e=a.find(d);
  cout<<e;
}

例子中的c的返回值是3 因为在a中找到了子串b,并且它的下标是3(默认下标从0开始寻找)
而e=a.find(d)的返回值是一个-1,因为他没有在a中找到d
还有一个作用,那就是查找从指定位置开始的第一次出现的目标字符串:
什么意思呢?就是从你指定的某一项开始寻找子串,如果找到的话就返回它的下标,注意这里
返回的是下标,不是从某一项开始找的那个标志数 比如:

#include<bits/stdc++.h>
using namespace std;
int main() {
  ios::sync_with_stdio(false);
  cin.tie(false);
  cout.tie(false);
  string a,b;
  a="1234567";
  b="4567";
  int c=a.find(b,2);
  cout<<c;
  string d="13";
  int e=a.find(d);
  cout<<e;
}

对上面的代码进进行修改以后c的返回值仍然是3而不是1

2.find_first_of()

查找子串中的某个字符最先出现的位置。find_first_of()不是全匹配,而find()是全匹配

#include<bits/stdc++.h>
using namespace std;
int main() {
  ios::sync_with_stdio(false);
  cin.tie(false);
  cout.tie(false);
  string a,b;
  a="1234567";
  b="287";
  int c=a.find_first_of(b);
  cout<<c;
  string d="13";
  int e=a.find_first_of(d);
  cout<<e;
}

把上面的函数全部修改成find_first_of函数以后,将上面的参数改成287 就算不匹配也能返回第一项的位置
如果想跳过前几个字符直接从后面找的话,还可以在find_first_of函数加一个数字参数,这就和上面的find
函数一样了:不过这时候函数的返回值一个是6一个是0;

#include<bits/stdc++.h>
using namespace std;
int main() {
  ios::sync_with_stdio(false);
  cin.tie(false);
  cout.tie(false);
  string a,b;
  a="1234567";
  b="287";
  int c=a.find_first_of(b,4);
  cout<<c;
  string d="13";
  int e=a.find_first_of(d);
  cout<<e;
}

3.find_last_of()

这个函数与find_first_of()功能差不多,只不过find_first_of()是从字符串的前面往后面搜索,而find_last_of()是从字符串的后面往前面搜索。返回值还是下标,不过必须得全部匹配以后返回最后一位匹配的下标

#include<bits/stdc++.h>
using namespace std;
int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  string a,b;
  a="1234567";
  b="34";
  int c=a.find_last_of(b);
  cout<<c;
}

拿这个举个例子把,这样写的话因为第四位,即下标是3的时候才能完全匹配,所以返回的是3
如果要把4删去,返回的就是2,因为直接匹配了 然后就返回

4.rfind()函数
这个函数可以从a字符串的最后开始找子串,返回正序最后一次匹配的地方 eg:

#include<bits/stdc++.h>
using namespace std;
int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  string a,b;
  a="341234567";
  b="34";
  int c=a.rfind(b);
  cout<<c;
}

此时在a中出现了两次b,但是最后一次出现的下标是4,所以返回4而不返回0,挺有用的

5.find_first_not_of()

找到第一个不与子串匹配的位置
举例子:

#include<bits/stdc++.h>
using namespace std;
int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  string a,b;
  a="341234567";
  b="345";
  int c=a.find_first_not_of(b);
  cout<<c;
}

a是341234567 b是345 很显然前两项都是匹配的,所以返回的是第三项的下标,即2;

回归正题:

2651: 单词

题目描述

有N个单词和字符串T,按字典序输出以字符串T为前缀的所有单词。

输入

第一行包含一个整数N;接下来N行,每行一个单词,长度不超过20。最后一行为字符串T。

输出

按字典序升序输出答案。

样例输入
复制样例数据 6
na
no
ki
ki
ka
ku
k

样例输出
ka
ki
ki
ku

提示

对于60%的数据,满足1≤N≤1000;
对于100%的数据,满足1≤N≤10000且所有字符均为小写字母。

这题我尝试用传统方法做,开两个string数组,虽然string的特性是可以按字典序排序,但是发现我那样做只能找到第一位匹配的,如果多了就没办法寻找了,代码如下:

#include<bits/stdc++.h>
using namespace std;
int main() {
  int n;
  cin>>n;
  char c;
  string a[2000];
  string b[2000];
  for(int i=0;i<n;i++)
  cin>>a[i];
  cin>>c;
  int k=0;
  for(int i=0;i<n;i++) {
    if(a[i][0]==c) {
      b[k]=a[i];
      k++;
    }
  }
  sort(b,b+k);
  for(int i=0;i<k;i++)
  cout<<b[i]<<endl;
}

这个样例虽然过了,但是我只判断了第一项,其实用这个方法仔细一想也可以,每一次遇到匹配的就加一,然后两个for循环套一下,每次完全匹配就放到另一个string数组中,然后排序输出也是可以的哦 但是那也太麻烦了,提供给大家一组洛谷的输入输出样例就知道有多麻烦了:

输入:

100
wisfw
rb
zxitqbq
ptccakmjjoh
teremcxkcuadsjnkcd
wxqlgmlqeetl
hdbsegfs
tvknnru
qqzaxeiwfrkwggkbbqi
of
fncowmfsevqu
jkvjnzueo
eewtgocwkhetu
udaasgkwrljutkhhj
ptccmsm
fsdlpaeh
jwbjt
wswb
rq
ptcckxivrzjsoxcdcgixllxj
lsfweufkbscvp
bvuyxkovinjzyajrl
nxbvj
lgunb
zhmgwogx
ptcccev
niyjeqwmfa
tuombvgeloyb
rjubdwksvss
ptccmjnh
dkvcihklrj
xecpvqbtxbdyyuzk
sxoklnzebdzoucerlo
ulrmojhpfoadi
ptccpvt
kfozrnaunjczy
ptcckeeyrjyvwbsljpe
ptccunxgczxtxelam
kfphtzlkcu
ewgwaf
psqevzric
tpe
musf
hhxezht
xxsvwybigolsbpvmfz
ptccfh
nvfjzfftnjm
xcnlkbzpsuvav
muwfqcdipohjhmyxqkzx
yvvnuoyvrw
ptccb
zkwqzkfzjooqppbluewe
ptcclt
ptccbaqzeytgpnqkscreoh
votcxsogpopkjyv
ptccqbmo
ptccqitcpupnzvgvnynw
tqzhcwewxyhemlh
tsgb
ptccrdyfvhpiz
dqxjimytgh
ptcczrn
efuzryakahlqyymtadl
ptccdkguhrbldphmjqnx
bcecssfvtzwxgufl
dypcvvx
ptccfxpqxtogr
ptccgnmzzccskdgzlknckpsl
qfcvkd
v
taapyfojsiaxr
ptccmmbmrxybinsvhtwdmdo
gopj
bxursvhqckh
ymsokijioarxxwkfimwv
ptccqiymyadvofprnl
ptccivakendfjlmx
ptccswydhxayxqdtnelnbw
aimk
ptccndfpmsbgijgbzz
ptcckpbmxyzx
cfhjkoezzfuouosjzv
tofrmkungwfpchjpyfk
dbhxfeniy
uanhmsqhpwiifrmrcfcu
maznpifjqokmoay
itrr
tgqnkjd
fmexumzcamofem
inxarkcqmosppvl
bpzlmn
k
xjzkdvnzyybyu
ro
uchfbagqi
ptccjvrziqjojneqyhz
zpiouboncchqrcvakwnh
hxgszx
rltpqixvykdlbzqo
kmshsfjjtpcjam
ptcc

输出:

ptccakmjjoh
ptccb
ptccbaqzeytgpnqkscreoh
ptcccev
ptccdkguhrbldphmjqnx
ptccfh
ptccfxpqxtogr
ptccgnmzzccskdgzlknckpsl
ptccivakendfjlmx
ptccjvrziqjojneqyhz
ptcckeeyrjyvwbsljpe
ptcckpbmxyzx
ptcckxivrzjsoxcdcgixllxj
ptcclt
ptccmjnh
ptccmmbmrxybinsvhtwdmdo
ptccmsm
ptccndfpmsbgijgbzz
ptccpvt
ptccqbmo
ptccqitcpupnzvgvnynw
ptccqiymyadvofprnl
ptccrdyfvhpiz
ptccswydhxayxqdtnelnbw
ptccunxgczxtxelam
ptcczrn

就算能做,也太麻烦了,所以我选择find函数,如果返回值是0的话就可以放到数组里面,然后很方便的排序输出了!!! 代码如下:

#include<bits/stdc++.h>
using namespace std;
int main() {
  string a[20000];
  string b;
  int n;
  cin>>n;
  for(int i=0;i<n;i++)
    cin>>a[i];
  cin>>b;
  sort(a,a+n);
  for(int i=0;i<n;i++) {	
    if(a[i].find(b)==0)
    cout<<a[i]<<endl;
  }
}

总结:在数据小的时候感觉find函数都能替代kmp算法,但是数据量一大肯定废了,虽然这两种的实现都是线性的,但是要是做那种重复字符串的题目,用find函数必须要在for外面套一个while 这样就变成了n^2的级别了,还是得用kmp啊 正好昨天在机房听完马大佬的讲课,听懂了kmp算法 先把自己打的代码贴上,之后再详细的说一下思考过程吧!

先说一下kmp的实现包括两个过程,第一个是自己匹配自己,找一下next数组,如果有大于0的next[i]出现就说明子串中有重复的部分,这时候kmp算法的优势就显现出来了:太快了!!!、
先贴代码,之后再详细说明:

#include<bits/stdc++.h>
using namespace std;
char a[100010];
char b[100010];
int next[100010];
int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  cin>>a+1>>b+1;
  int la=strlen(a+1);
  int lb=strlen(b+1);
  int j=0;
  for(int i=2;i<=lb;i++) {
    while(j&&b[i]!=b[j+1])
    j=next[j];
    if(b[i]==b[j+1])
    j++;
    next[i]=j;
  }
  j=0;
  for(int i=1;i<=la;i++) {
    while(j&&a[i]!=b[j+1])
    j=next[j];
    if(a[i]==b[j+1])
    j++;
    if(j==lb
    ) {
      cout<<i-j+1<<endl;
      j=next[j];
    }
  }
  for(int i=1;i<=lb;i++)
  cout<<next[i]<<" ";
}

这篇博客近期就会更新,也许就是今晚哦~ 敬请期待

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值