【数据结构与算法】字符串匹配(头歌习题)【合集】

目录

  • 第1关:实现朴素的字符串匹配
    • 任务描述
    • 相关知识
    • 编程要求
    • 评测说明
    • 完整代码
  • 第2关:实现KMP字符串匹配
    • 任务描述
    • 相关知识
    • 编程要求
    • 评测说明
    • 完整代码
  • 第3关:【模板】KMP算法
    • 任务描述
    • 相关知识
      • C++ STL容器string
        • 1、string的定义
        • 2、string中内容的访问
        • 3、string常用函数实例解析
        • 4、C语言中将字符串转换为数值的函数
        • 5、C++11中将string转换为数值类型
        • 6.不同编译器打开-std=c++11编译开关的方法:
    • 编程要求:
    • 完整代码
  • 第4关:利用kmp算法求子串在主串中不重叠出现的次数
    • 任务描述
    • 编程要求
    • 完整代码
  • 第5关:利用KMP算法求子串在主串中重叠出现的次数
    • 任务描述
    • 编程要求
    • 完整代码

第1关:实现朴素的字符串匹配

任务描述

本关任务是实现函数int FindSubStr(char* t, char* p)。

相关知识

在一个长字符串中寻找一个短字符串出现的位置,这是字符串匹配问题。

例如:长字符串是 “string” ,短字符串是 “ring” ,那么短字符串在长字符串中出现的位置是 2 ,即 “ring” 在 “string” 中出现的开始位置是 2 。

编程要求

本关的编程任务是补全 step1/mystr.cpp 文件中的FindSubStr函数,以实现朴素的字符串匹配。

具体请参见后续测试样例。
本关涉及的代码文件 mystr.cpp 的代码框架如下:

int FindSubStr(char* t, char* p)
/*
从字符串t查找子字符串p。
字符串以数值结尾,例如p="str",那么p[0]='s',p[1]='t',p[2]='r',p[3]=0。
采用朴素的匹配算法,返回子字符串第一次出现的位置,例如t="string ring",p="ring",则返回2。
若没有找到,则返回-1。
*/
{// 请在此添加代码,补全函数FindSubStr/********** Begin *********//********** End **********/
}

评测说明

本关的测试文件是 step1/Main.cpp ,测试过程如下:

平台编译 step1/Main.cpp ,然后链接相关程序库并生成 exe 可执行文件;
平台运行该 exe 可执行文件,并以标准输入方式提供测试输入;
平台获取该 exe 可执行文件的输出,然后将其与预期输出对比,如果一致则测试通过;否则测试失败。
以下是平台对 step1/Main.cpp 的样例测试集:

样例输入:
string
tri
样例输出:
Location: 1

开始你的任务吧,祝你成功!

完整代码

/*************************************************************date: April 2009copyright: Zhu EnDO NOT distribute this code.
**************************************************************/int FindSubStr(char* t, char* p)
/*
从字符串t查找子字符串p。
字符串以数值结尾,例如p="str",那么p[0]='s',p[1]='t',p[2]='r',p[3]=0。
采用朴素的匹配算法。
返回子字符串第一次出现的位置,例如t="string ring",p="ring",则返回2。
若没有找到,则返回-1。
*/
{// 请在此添加代码,补全函数FindSubStr/********** Begin *********/int cnt=0;char* tr; while(*t!='\0' && *p!='\0'){if(*t!=*p){t++;cnt++;}else{tr=t;t++;p++;while(*p!='\0'){if(*t==*p){t++;p++;}}if(*p=='\0'){return cnt;}else{t = tr;p = &p[0];}}}if(*t=='\0'){return -1;}/********** End **********/
}

在这里插入图片描述

第2关:实现KMP字符串匹配

任务描述

本关的编程任务是补全 step2/kmp.cpp 文件中的KmpGenNext函数,以实现 KMP 字符串匹配。该函数生成给定字符串的next数组。

相关知识

第 1 关中实现的朴素的字符串匹配算法在实际应用系统中效率低,而 KMP 字符串匹配算法可以实现高效的匹配。

假设长字符串为t,短字符串为p。为了进行 KMP 匹配,首先需要计算字符串p的next数组,后面实现了计算该数组的函数void KmpGenNext(char* p, int* next)。对于 “abcabcab” ,计算出的next数组如下图:
请添加图片描述

其中:next[i]给出如下信息:从左到右将p的字符与t的字符进行比对时,若在p的i号位置出现不匹配,就将字符串p相对t右移i-next[i]位;若next[i]>=0,则右移后比对位置从next[i]号位置开始,否则从0号位置开始。下图 1 给出了一个匹配示例:

请添加图片描述

本关涉及两个函数:

void KmpGenNext(char* p, int* next)
// 生成p的next数组, next数组长度大于等于字符串p的长度加1。
int KmpFindSubWithNext(char* t, char* p, int* next)
// 从t中查找子串p的第一次出现的位置。
// 若找到,返回出现的位置,否则返回-1。

编程要求

本关的编程任务是补全 step2/kmp.cpp 文件中的KmpGenNext函数,以实现 KMP 字符串匹配。该函数生成给定字符串的next数组,生成算法请你查阅相关资料。

具体请参见后续测试样例。
本关涉及的代码文件 kmp.cpp 的代码框架如下:

#include <stdio.h>
#include <stdlib.h>
#include "kmp.h"
///
void KmpGenNext(char* p, int* next)
// 生成p的next数组, next数组长度大于等于字符串p的长度加1
{// 请在此添加代码,补全函数KmpGenNext/********** Begin *********//********** End **********/
}
int KmpFindSubWithNext(char* t, char* p, int* next)
// 从t中查找子串p的第一次出现的位置
// 若找到,返回出现的位置,否则返回-1
{int i=0, j=0;while(p[i]!=0 && t[j]!=0)    {if(p[i]==t[j])     { i++;  j++; }else  if (next[i]>=0) {i = next[i];}else  { i=0;  j++; }}if(p[i]==0)  return j-i; //foundelse  return -1;  //not found
}

评测说明

本关的测试文件是 step2/Main.cpp ,测试过程如下:

平台编译 step2/Main.cpp ,然后链接相关程序库并生成 exe 可执行文件;
平台运行该 exe 可执行文件,并以标准输入方式提供测试输入;
平台获取该 exe 可执行文件的输出,然后将其与预期输出对比,如果一致则测试通过;否则测试失败。

输入输出格式:
输入格式:
第一行输入母串
第二行输入子串

输出格式:
输出Location: #,其中#是子串在母串中的位置编号

以下是平台对 step2/Main.cpp 的样例测试集:
样例输入:
stringabcedf1stringabcdef2stringabcdef3stringabcdef4stringabcdef5stringabcdef6stringabcdef7
stringabcdef7

样例输出:
Location: 78

开始你的任务吧,祝你成功!

完整代码

/*************************************************************date: copyright: Zhu EnDO NOT distribute this code without my permission.
**************************************************************/
//字符串 实现文件
//
#include <stdio.h>
#include <stdlib.h>
#include "kmp.h"
#include<string.h>
/void KmpGenNext(char* p, int* next)
//生成p的next数组, next数组长度大于等于字符串p的长度加1
{// 请在此添加代码,补全函数KmpGenNext/********** Begin *********/int j,k;  // j遍历t, k求t[j]前与开头相同的字符个数k = -1;j = 0;next[0] = -1;while(j<strlen(p)-1){  if(k==-1 || p[j]==p[k]){j++;k++;next[j] = k;}else{k=next[k];}}/********** End   *********/
}int KmpFindSubWithNext(char* t, char* p, int* next)
//从t中查找子串p的第一次出现的位置
//若找到,返回出现的位置,否则返回-1
{int i=0, j=0;while(p[i]!=0 && t[j]!=0)	{if(p[i]==t[j]) 	{ i++;  j++; }else  if (next[i]>=0) {i = next[i];}else  { i=0;  j++; }}if(p[i]==0)  return j-i; //foundelse  return -1;  //not found
}

在这里插入图片描述

第3关:【模板】KMP算法

任务描述

本关任务: 给出两个字符串text和pattern,其中pattern为text的子串,求出pattern在text中所有出现的位置。

为了减少骗分的情况,接下来还要输出子串的前缀数组next。

相关知识

为了完成本关任务,你需要了解:C++ STL中字符串容器string的使用。

C++ STL容器string

在C语言中,一般使用字符数组char str[]来存放字符串,但是使用字符数组有时会显得操作麻烦,而且容易因经验不足而产生一些错误。为了使编程者可以更方便地对字符串进行操作,C++在STL中加入了 string类型,对字符串常用的需求功能进行了封装,使得操作起来更方便,且不易出错。
如果要使用 string,需要添加 string 头文件,即#include (注意 string.h 和 string 是不一样的头文件)。除此之外,还需要在头文件下面加上一句:“using namespace std;”,这样就可以在代码中使用string 了。下面来看string的一些常用用法。

1、string的定义

定义string的方式跟基本数据类型相同,只需要在string后跟上变量名即可:
string str;
如果要初始化,可以直接给string类型的变量进行赋值:

string str = “abcd”;
//或者:
char* p = “hello”;
string str§;

2、string中内容的访问

(1) 通过下标访问
一般来说,可以直接像字符数组那样去访问string,要注意的是,string中字符的索引值从0开始计算。

#include <stdio.h>
#include <string>
using namespace std;
int main() {string str = "abcd";for (int i = 0; i < str.length(); i++) {printf("%c", str[i]);        //输出abcd}return 0;
}

输出结果:abcd

也可以使用string的成员函数at(int index)访问index位置的字符。例如:在上面的示例中,str[i]等价于str.at(i)。
如果要读入和输出整个字符串,则只能使用cin和cout:

#include <iostream>    //cin和cout都在iostream头文件中,而不是stdio.h中
#include <string>
using namespace std;
int main() {string str;cin >> str;cout << str;return 0;
}

上面的代码对任意的字符串输入,都会输出同样的字符串。
那么,真的没有办法用printf输出string吗?其实是有的,即用c_str( )将string类型转换为字符数组进行输出,示例如下:

#include <stdio.h>
#include <string>
using namespace std;
int main() {string str = "abcd";printf("%s\n", str.c_str());return 0;
}

输出结果:abcd

(2)通过迭代器访问
一般仅通过(1)即可满足访问的要求,但是有些函数比如insert( )与erase( )则要求以迭代器为参数,因此还是需要学习一下string迭代器的用法。
由于string不像其他STL容器那样需要参数,因此可以直接如下定义:
string::iterator it;
这样就得到了迭代器it,并且可以通过*it来访问string里的每一个字符:

#include <stdio.h>
#include <string>
using namespace std;
int main() {string str = "abcd";string::iterator it;for (it = str.begin(); it != str.end(); it++) {printf("%c", *it);}return 0;
}

输出结果:abcd

最后指出:string和vector 一样,支持直接对迭代器进行加减某个数字,如str.begin( ) + 3的写法是可行的。

3、string常用函数实例解析

事实上,string的函数有很多,伹是有些函数并不常用,因此下面就一些常用的函数举例。

(1)operator+=
这是string的加法,可以将两个`string直接拼接起来。
示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {string str1 = "abcd";string str2 = "xyz";string str3 = str1 + str2;    //将str1和str2拼接,赋值给str3str1 += str2;                    //将str2直接拼接到str1上cout << str1 << endl;cout << str3 << endl;return 0;
}

输出结果:
abcdxyz
abcdxyz

(2)比较运算符(compare operator)
两个string类型可以直接使用==、!=、<、<=、>、>=比较大小,比较规则是字典序。
示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {string str1 = "aa";string str2 = "aaa";string str3 = "abc";string str4 = "xyz";if (str1 < str2)                //如果str1字典序小于str2cout << "ok1" << endl;        //则输出ok1if (str1 != str3)                //如果str1字典序和str3不等cout << "ok2" << endl;    //则输出ok2if (str4 >= str3)                //如果str4字典序大于等于str3cout << "ok3" << endl;    //则输出ok3return 0;
}

输出结果:
ok1
ok2
ok3

(3)length( ) 和size( )
length( )返回string的长度,即存放的字符数,时间复杂度为O(1)。size( )和length( )功能相同。
示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {string str = "abcdxyz";cout << str.length() << " " << str.size() << endl;return 0;
}

输出结果:
7 7

(4)insert( )
string的insert( )函数有多种使用方法,其时间复杂度都是O(n),其中n是字符串的长度。
① insert(pos,string): 在pos位置插入字符串string。
示例如下:

string str = “abcxyz”;
str.insert(3, “789”);
cout << str << endl;
输出结果:
abc789xyz
② insert(it, it2, it3): it为原字符串的待插入位置,it2和it3为待插入的字符串的首尾迭代器,用来表示待插入的字符串[it2, it3)将被插在it的位置上。
示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {string str1 = "abcxyz";        //str1为原字符串string str2 = "789";            //str2为待插入的字符串//在str1的3号位,即在字符x的位置插入字符串789str1.insert(str1.begin() + 3, str2.begin(), str2.end());cout << str1 << endl;return 0;
}

输出结果:abc789xyz

(5)erase( )
erase( )有两种用法:删除单个字符、删除一个区间内的所有字符。时间复杂度均为O(n),其中n为字符串的长度。
① 删除单个字符
string str;
str.erase(it)用于删除单个字符,it为需要删除的字符的迭代器。
示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {string str = "abcdefg";str.erase(str.begin() + 4);    //删除4号位的字符,即字符ecout << str << endl;return 0;
}

输出结果:abcdfg

② 删除一个区间内的所有元素。
删除一个区间内的所有元素有两种方法:
第一种方法:str.erase(first, last),其中first为需要删除的区间的起始迭代器,而last则为需要删除的区间的末尾迭代器的下一个地址,也即为删除[first, last)。 示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {string str = "abcdefg";str.erase(str.begin() + 2, str.end() - 1);    //删除cdefcout << str << endl;return 0;
}

输出结果:abg

第二种方法:str.erase(pos, length),其中pos为需要开始删除的起始位置,length为删除的字符个数。
示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {string str = "abcdefg";str.erase(3, 2);    //删除从3号位置开始的2个字符,即删除decout << str << endl;return 0;
}

输出结果:abcfg

(6)clear( )
clear( )用于清空string中的数据,时间复杂度一般为O(1)。
示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {string str = "abcde";str.clear();            //清空字符串cout << str.length() << endl;return 0;
}

输出结果:0

(7)substr( )
substr(pos, len)返回从pos位置开始、长度为len的子串。如果第二个参数len没有给出,则一直取到末尾。
示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {string str = "Thank you for your smile.";cout << str.substr(0, 5) << endl;            //Thankcout << str.substr(14, 4) << endl;            //yourcout << str.substr(19, 5) << endl;            //smilecout << str.substr(14) << endl;                //your smile.return 0;
}

输出结果:
Thank
your
smile
your smile.

(8)string::npos
string::npos是一个常数,其本身的值为-1,但是由于是unsigned int类型,因此实际上也可以认为是unsigned int类型的最大值。string::npos一般用作find函数失败时的返回值。例如在下面的示例中可以认为string::npos等于-1或者是4294967295(这个值就是unsigned int类型的最大值2
32
−1)。
示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {if (string::npos == -1) {cout << "-1 is true." << endl;}if (string::npos == 4294967295) {cout << "4294967295 is also true." << endl;}return 0;
}

输出结果:
-1 is true.
4294967295 is also true.

(9)find( )
find( )函数有2个重载的使用形式,如下:
① str.find(str2):当str2是str的子串时,返回其在str中第一次出现的位置;如果str2不是str的子串,那么返回string::npos。
② str.find(str2, pos):从str的pos位置开始匹配str2,返回值与上面相同。
示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {string str = "Thank you for your smile.";string str2 = "you";string str3 = "me";if (str.find(str2) != string::npos) {cout << str.find(str2) << endl;}if (str.find(str2, 7) != string::npos) {cout << str.find(str2, 7) << endl;}if (str.find(str3) != string::npos) {cout << str.find(str3) << endl;} else {cout << "I know there is no position for me." << endl;}return 0;
}

输出结果:
6
14
I know there is no position for me.

(10)replace( )
str.replace(pos, len, str2)把str从pos位置开始、长度为len的子串替换为str2。
str.replace(it1, it2, str2)把str的迭代器[it1, it2)范围的子串替换为str2。
示例如下:

#include <iostream>
#include <string>
using namespace std;
int main() {string str = "Maybe you will turn around.";string str2 = "will not";string str3 = "surely";cout << str.replace(10, 4, str2) << endl;cout << str.replace(str.begin(), str.begin() + 5, str3) << endl;return 0;
}

输出结果:
Maybe you will not turn around.
surely you will not turn around.

(11)reverse()
reverse(): 逆转字符串。string类本身没有提供逆转字符串的成员函数,而是STL算法逆转字符串。实际上,reverse()函数用来逆转序列化容器(如vector、list,包括C/C++的数组)。
使用该函数,需要增加头文件:#include ,该头文件包含次函数的声明。
函数原型如下:
void reverse ( BidirectionalIterator first, BidirectionalIterator last);
示例:

#include <iostream>
#include <string>
#include <algorithm>    //使用reverse函数
using namespace std;
int main(int argc, char const *argv[])
{string s = "hello";reverse(s.begin(), s.end());cout << s << endl;return 0;
}

输出结果:
olleh

4、C语言中将字符串转换为数值的函数

注意:在使用下面的函数时,需要包含头文件#include <stdlib.h>``。 (1) atoi( ): 将字符串转换为整数。函数原型如下: int atoi(const char* s);`
示例如下:

/* atoi example */
#include <stdio.h>
#include <stdlib.h>
int main ()
{int i;char szInput [256];                //定义字符数组printf ("Enter a number: ");scanf("%s",szInput);            //输入一个数值字符串i = atoi (szInput);                //转换为整数printf ("The value entered is %d\n", i);return 0;
}

运行结果1:
Enter a number: 234
The value entered is 234
运行结果2:
Enter a number: 234.567
The value entered is 234
运行结果3:
Enter a number: 234abc567
The value entered is 234

``(2) atol( ): 将字符串转换为长整数(long int)。函数原型如下: long int atol(const char* s);`
示例如下:

/* atol example */
#include <stdio.h>
#include <stdlib.h>
int main ()
{int li;char szInput [256];                //定义字符数组printf ("Enter a long number: ");scanf("%s",szInput);            //输入一个数值字符串li = atol (szInput);            //转换为长整数printf ("The value entered is %d\n", li);return 0;
}

运行结果:
Enter a number: 23456789
The value entered is 23456789

(3) atof( ): 将字符串转换为double类型。函数原型如下:
double atof(const char* s);
示例如下:

/* atof example: sine calculator */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main ()
{double n, m;double pi = 3.1415926535;char szInput [256];                //定义字符数组printf ( "Enter degrees: " );    gets ( szInput );                //输入一个角度字符串n = atof ( szInput );            //转换为double类型m = sin (n * pi / 180);        //求正弦函数printf ( "The sine of %f degrees is %f\n" , n, m );return 0;
}

运行结果:
Enter degrees: 45
The sine of 45.000000 degrees is 0.707107

其他还有:
strtol: Convert string to long integer (function )
strtoul: Convert string to unsigned long integer (function )
strtod: Convert string to double (function )
不再一一举例。更多详情,参见下面的网址:
http://www.cplusplus.com
在该网址下面的“C Library”分支下面有所有C语言的库函数:
http://www.cplusplus.com/reference/clibrary/

5、C++11中将string转换为数值类型

注意:下面的函数在使用时,要包含头文件#include ,并使用using namespace std; 同时还需要将所使用的编译器的编译开关-std=c++11打开。

(1) stoi( ): 将string转换为int。
示例如下:

// stoi example
#include <iostream>
#include <string>
using namespace std;
int main ()
{int i;string str;                        //定义string类型的字符串cout << "Enter a number: ";cin >> str;                        //输入一个string类型的字符串i = stoi ( str );                //转换为int类型cout << "The value entered is " << i << endl;return 0;
}

运行结果:
Enter a number: 234567
The value entered is 234567

(2) stol( ): 将string转换为long int。
(3) stof( ): 将string转换为double。
其他还有:

stoul: Convert string to unsigned integer (function template )
stoull: 将string转换为unsigned long long
不再一一举例。更多详情,参见下面的网址:
http://www.cplusplus.com

6.不同编译器打开-std=c++11编译开关的方法:

1、 Dev-Cpp
请添加图片描述
请添加图片描述

2、CodeBlocks
请添加图片描述
请添加图片描述

编程要求:

给出两个字符串text和pattern,其中pattern为text的子串,求出pattern在text中所有出现的位置。

为了减少骗分的情况,接下来还要输出子串的前缀数组next。

输入格式:
第一行为一个字符串,即为text。

第二行为一个字符串,即为pattern。

输出格式:
若干行,每行包含一个整数,表示pattern在text中出现的位置。

接下来1行,包括length(pattern)个整数,表示前缀数组next[i]的值,数据间以一个空格分隔,行尾无多余空格。

输入样例:
ABABABC
ABA

输出样例:
1
3
-1 0 0

样例说明:
kmp算法匹配示例
请添加图片描述
提示: 对于100%的数据:text长度<=1000000,pattern长度<=1000000

开始你的任务吧,祝你成功!

完整代码

#include <iostream>
#include <string>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;/*** txt: 字符串* pat: 模式串* 输出pat在txt中出现的位置* 输出next[]数组的值*/
const int N = 1e6 + 1;
int lps[N];
int nextarray[N];void get_lps(string pat, int * lps) {int m = pat.length();int j = 0, i = 1;lps[0] = 0;while (i < m) {if (pat[j] == pat[i]) {j++;lps[i] = j;i++;} else if (j != 0) {j = lps[j - 1];} else {lps[i] = 0;i++;}}
}void get_next(string pat,int* lps)
{int m = pat.length();get_lps(pat,lps);int i;nextarray[0] = -1;for(i = m - 2; i >= 0 ; i--){lps[i+1] = lps[i];}lps[0]= -1;for(i=0;i<m;i++){printf("%d",lps[i]);if(i<m-1)printf(" ");}
}void KMP(string txt, string pat)
{//请在下面编写代码/*************************Begin*********************/int n = txt.length();int m = pat.length();int i = 0;int j = 0;get_lps(pat, lps);while (i < n && j < m) {if (txt[i] == pat[j]) {i++; j++;} else if (j != 0) {j = lps[j - 1];} else {i++;}if (j == m) {cout << i - j + 1 << endl;j = lps[j - 1];}}get_next(pat,lps);/**************************End**********************/
}int main(int argc, char const *argv[])
{string txt, pat;cin >> txt >> pat;  //输入文本串和模式串KMP(txt, pat);    //调用kmp算法输出模式串在文本串中的位置以及next[]数组的值return 0;
}

在这里插入图片描述

第4关:利用kmp算法求子串在主串中不重叠出现的次数

任务描述

本关任务:编写一个程序,利用kmp算法求子串在主串中不重叠出现的次数。

实验目的:深入掌握KMP算法的应用。
实验内容:编写一个程序,利用KMP算法求子串t在主串s中出现的次数,例如:s=“aabbdaabbde”,t=“aabbd”,t在s中出现2次;再例如:s=“aaaaa”,t=“aa”,t在s中出现2次。
实验工具:本关提供顺序串SqString的基本运算及其实现(在头文件sqstring.h中);您也可以直接使用C++ STL提供的string容器。

编程要求

根据提示,在右侧编辑器补充完成代码,计算并输出字符串t在字符串s中不重叠出现的次数。

测试说明
平台会对你编写的代码进行测试:

输入格式
输入包括2行。
第一行为字符串s,长度不超过10^6 。
第二行为字符串t,长度不超过10^6 。

输出格式
在一行中输出t在s中出现的次数。

样例输入1
aaaaa
aa

样例输出1
2

样例输入2
aabbdaabbde
aabbd

样例输出2
2

开始你的任务吧,祝你成功!

完整代码

/* step4/step4.cpp  作答区文件 */#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>       //C++ STL之string容器
using namespace std;#include "sqstring.h" //包含顺序串SqString基本运算算法//利用KMP算法求t在s中出现的次数
int Count(string s, string t); //利用KMP算法求t在s中出现的次数
int Count(char* s, char* t); //利用KMP算法求t在s中出现的次数
int Count(SqString s, SqString t); /*** 说明:任选上面三个函数中的一个进行实现。*///请在下面编写代码const int N =1e6;
int lps[N];
int nt[N];
void get_lps(string t,int *lps)
{int m=t.size();int i=1,j=0;lps[0]=0;while(i<m){if(t[i]==t[j]){++j;lps[i]=j;i++;}else if(j !=0){j=lps[j-1];}else{lps[i]=0;i++;}}
}
void get_next(string t,int *next)
{int m=t.length();int k= -1,j=0;next[0]=-1;while(j<m-1){if(k==-1||t[k]==t[j]){k++;j++;next[j]=k;}elsek = next[k];}
}
int Count(string s,string t)
{int n=s.length();int m=t.length();int i=0;int j=0;int tot=0;get_lps(t,lps);while(i<n&&j<m){if(s[i]==t[j]){i++;j++;}else if(j!=0)j=lps[j-1];else i++;if(j==m){tot++;j=0;}}return tot;
}
int main()
{string s,t;cin>>s>>t;cout<<Count(s,t)<<endl;return 0;
}
/* step4/sqstring.h 头文件 */#ifndef __SQSTRING_H
#define __SQSTRING_H//顺序串基本运算的算法
#include <stdio.h>
#define MaxSize 1000001
typedef struct
{char data[MaxSize];		//串中字符int length;				//串长
} SqString;					//声明顺序串类型
void StrAssign(SqString &s, char cstr[])	//字符串常量赋给串s
{int i;for (i = 0; cstr[i] != '\0'; i++)s.data[i] = cstr[i];s.length = i;
}
void DestroyStr(SqString &s)		//销毁串
{  }void StrCopy(SqString &s, SqString t)	//串复制
{for (int i = 0; i < t.length; i++)s.data[i] = t.data[i];s.length = t.length;
}
bool StrEqual(SqString s, SqString t)	//判串相等
{bool same = true;if (s.length != t.length)				//长度不相等时返回0same = false;elsefor (int i = 0; i < s.length; i++)if (s.data[i] != t.data[i])	//有一个对应字符不相同时返回假{	same = false;break;}return same;
}
int StrLength(SqString s)	//求串长
{return s.length;
}
SqString Concat(SqString s, SqString t)	//串连接
{SqString str;int i;str.length = s.length + t.length;for (i = 0; i < s.length; i++)	//s.data[0..s.length-1]→strstr.data[i] = s.data[i];for (i = 0; i < t.length; i++)	//t.data[0..t.length-1]→strstr.data[s.length + i] = t.data[i];return str;
}
SqString SubStr(SqString s, int i, int j)	//求子串
{SqString str;int k;str.length = 0;if (i <= 0 || i > s.length || j < 0 || i + j - 1 > s.length)return str;					//参数不正确时返回空串for (k = i - 1; k < i + j - 1; k++)  		//s.data[i..i+j]→strstr.data[k - i + 1] = s.data[k];str.length = j;return str;
}
SqString InsStr(SqString s1, int i, SqString s2)	//插入串
{int j;SqString str;str.length = 0;if (i <= 0 || i > s1.length + 1)		//参数不正确时返回空串return str;for (j = 0; j < i - 1; j++)      		//s1.data[0..i-2]→strstr.data[j] = s1.data[j];for (j = 0; j < s2.length; j++)		//s2.data[0..s2.length-1]→strstr.data[i + j - 1] = s2.data[j];for (j = i - 1; j < s1.length; j++)		//s1.data[i-1..s1.length-1]→strstr.data[s2.length + j] = s1.data[j];str.length = s1.length + s2.length;return str;
}
SqString DelStr(SqString s, int i, int j)		//串删去
{int k;SqString str;str.length = 0;if (i <= 0 || i > s.length || i + j > s.length + 1) //参数不正确时返回空串return str;for (k = 0; k < i - 1; k++)       		//s.data[0..i-2]→strstr.data[k] = s.data[k];for (k = i + j - 1; k < s.length; k++)	//s.data[i+j-1..s.length-1]→strstr.data[k - j] = s.data[k];str.length = s.length - j;return str;
}
SqString RepStr(SqString s, int i, int j, SqString t)	//子串替换
{int k;SqString str;str.length = 0;if (i <= 0 || i > s.length || i + j - 1 > s.length) //参数不正确时返回空串return str;for (k = 0; k < i - 1; k++)				//s.data[0..i-2]→strstr.data[k] = s.data[k];for (k = 0; k < t.length; k++)   		//t.data[0..t.length-1]→strstr.data[i + k - 1] = t.data[k];for (k = i + j - 1; k < s.length; k++)	//s.data[i+j-1..s.length-1]→strstr.data[t.length + k - j] = s.data[k];str.length = s.length - j + t.length;return str;
}
void DispStr(SqString s)	//输出串s
{if (s.length > 0){	for (int i = 0; i < s.length; i++)printf("%c", s.data[i]);printf("\n");}
}#endif

在这里插入图片描述

第5关:利用KMP算法求子串在主串中重叠出现的次数

任务描述

本关任务:编写一个程序,利用kmp算法求子串在主串中不重叠出现的次数。

实验目的:深入掌握KMP算法的应用。
实验内容:编写一个程序,利用KMP算法求子串t在主串s中重叠出现的次数,例如:s=“aabbdaabbde”,t=“aabbd”,t在s中出现2次;再例如:s=“aaaaa”,t=“aa”,t在s中出现4次。
实验工具:本关提供顺序串SqString的基本运算及其实现(在头文件sqstring.h中);您也可以直接使用C++ STL提供的string容器。

编程要求

根据提示,在右侧编辑器补充完成代码,计算并输出字符串t在字符串s中重叠出现的次数。

测试说明
平台会对你编写的代码进行测试:

输入格式
输入包括2行。
第一行为字符串s,长度不超过10^6 。
第二行为字符串t,长度不超过10^6 。

输出格式
在一行中输出t在s中出现的次数。

样例输入1
aaaaa
aa

样例输出1
4

样例输入2
aabbdaabbde
aabbd

样例输出2
2

开始你的任务吧,祝你成功!

完整代码

/* step5/step5.cpp 作答区文件*/#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>       //C++ STL之string容器
using namespace std;#include "sqstring.h" //包含顺序串SqString基本运算算法//利用KMP算法求t在s中重叠出现的次数
int Count(string s, string t); //利用KMP算法求t在s中重叠出现的次数
int Count(char* s, char* t); //利用KMP算法求t在s中重叠出现的次数
int Count(SqString s, SqString t); /*** 说明:任选上面三个函数中的一个进行实现。*///请在下面编写代码
const int N =1e6;
int lps[N];
int nt[N];
void get_lps(string t,int *lps)
{int m=t.size();int i=1,j=0;lps[0]=0;while(i<m){if(t[i]==t[j]){++j;i++;}else if(j !=0){j=lps[j-1];}else{lps[i]=0;i++;}}
}
void get_next(string t,int *next)
{int m=t.length();int k= -1,j=0;next[0]=-1;while(j<m-1){if(k==-1||t[k]==t[j]){k++;j++;next[j]=k;}elsek = next[k];}
}
int Count(string s,string t)
{int n=s.length();int m=t.length();int i=0;int j=0;int tot=0;get_next(t,nt);while(i<n&&j<m){if(j==m-1&&s[i]==t[j]){tot++;j=nt[j];}if(j==-1||s[i]==t[j]){i++;j++;}else j=nt[j];}return tot;
}
int main()
{string s,t;cin>>s>>t;cout<<Count(s,t)<<endl;return 0;
}
/*step5/sqstring.h 头文件*/
#ifndef __SQSTRING_H
#define __SQSTRING_H//顺序串基本运算的算法
#include <stdio.h>
#define MaxSize 1000001
typedef struct
{char data[MaxSize];		//串中字符int length;				//串长
} SqString;					//声明顺序串类型
void StrAssign(SqString &s, char cstr[])	//字符串常量赋给串s
{int i;for (i = 0; cstr[i] != '\0'; i++)s.data[i] = cstr[i];s.length = i;
}
void DestroyStr(SqString &s)		//销毁串
{  }void StrCopy(SqString &s, SqString t)	//串复制
{for (int i = 0; i < t.length; i++)s.data[i] = t.data[i];s.length = t.length;
}
bool StrEqual(SqString s, SqString t)	//判串相等
{bool same = true;if (s.length != t.length)				//长度不相等时返回0same = false;elsefor (int i = 0; i < s.length; i++)if (s.data[i] != t.data[i])	//有一个对应字符不相同时返回假{	same = false;break;}return same;
}
int StrLength(SqString s)	//求串长
{return s.length;
}
SqString Concat(SqString s, SqString t)	//串连接
{SqString str;int i;str.length = s.length + t.length;for (i = 0; i < s.length; i++)	//s.data[0..s.length-1]→strstr.data[i] = s.data[i];for (i = 0; i < t.length; i++)	//t.data[0..t.length-1]→strstr.data[s.length + i] = t.data[i];return str;
}
SqString SubStr(SqString s, int i, int j)	//求子串
{SqString str;int k;str.length = 0;if (i <= 0 || i > s.length || j < 0 || i + j - 1 > s.length)return str;					//参数不正确时返回空串for (k = i - 1; k < i + j - 1; k++)  		//s.data[i..i+j]→strstr.data[k - i + 1] = s.data[k];str.length = j;return str;
}
SqString InsStr(SqString s1, int i, SqString s2)	//插入串
{int j;SqString str;str.length = 0;if (i <= 0 || i > s1.length + 1)		//参数不正确时返回空串return str;for (j = 0; j < i - 1; j++)      		//s1.data[0..i-2]→strstr.data[j] = s1.data[j];for (j = 0; j < s2.length; j++)		//s2.data[0..s2.length-1]→strstr.data[i + j - 1] = s2.data[j];for (j = i - 1; j < s1.length; j++)		//s1.data[i-1..s1.length-1]→strstr.data[s2.length + j] = s1.data[j];str.length = s1.length + s2.length;return str;
}
SqString DelStr(SqString s, int i, int j)		//串删去
{int k;SqString str;str.length = 0;if (i <= 0 || i > s.length || i + j > s.length + 1) //参数不正确时返回空串return str;for (k = 0; k < i - 1; k++)       		//s.data[0..i-2]→strstr.data[k] = s.data[k];for (k = i + j - 1; k < s.length; k++)	//s.data[i+j-1..s.length-1]→strstr.data[k - j] = s.data[k];str.length = s.length - j;return str;
}
SqString RepStr(SqString s, int i, int j, SqString t)	//子串替换
{int k;SqString str;str.length = 0;if (i <= 0 || i > s.length || i + j - 1 > s.length) //参数不正确时返回空串return str;for (k = 0; k < i - 1; k++)				//s.data[0..i-2]→strstr.data[k] = s.data[k];for (k = 0; k < t.length; k++)   		//t.data[0..t.length-1]→strstr.data[i + k - 1] = t.data[k];for (k = i + j - 1; k < s.length; k++)	//s.data[i+j-1..s.length-1]→strstr.data[t.length + k - j] = s.data[k];str.length = s.length - j + t.length;return str;
}
void DispStr(SqString s)	//输出串s
{if (s.length > 0){	for (int i = 0; i < s.length; i++)printf("%c", s.data[i]);printf("\n");}
}#endif

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/311026.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用YOLOv8和Grad-CAM技术生成图像热图

目录 yolov8导航 YOLOv8&#xff08;附带各种任务详细说明链接&#xff09; 概述 环境准备 代码解读 导入库 定义letterbox函数 调整尺寸和比例 计算填充 应用填充 yolov8_heatmap类定义和初始化 后处理函数 绘制检测结果 类的调用函数 热图生成细节 参数解释 we…

C++初阶——基础知识(函数重载与引用)

目录 1.命名冲突 2.命名空间 3.缺省参数 4.函数重载 1.函数重载的特点包括&#xff1a; 2.函数重载的好处包括&#xff1a; 3.引用 引用的特点包括 引用的主要用途包括 引用和指针 引用 指针 类域 命名空间域 局部域 全局域 第一个关键字 命名冲突 同一个项目之间冲…

sklearn 中matplotlib编制图表

代码 # 导入pandas库&#xff0c;并为其设置别名pd import pandas as pd import matplotlib.pyplot as plt# 使用pandas的read_csv函数读取名为iris.csv的文件&#xff0c;将数据存储在iris_data变量中 iris_data pd.read_csv(data/iris.txt,sep\t)# 使用groupby方法按照&quo…

期权二叉树估值与图计算

传统期权二叉树的算法都是基于数组的&#xff0c;对于没有编程基础的人来说非常不直观。二叉树是一种特殊的图&#xff0c;可以用python networkx这个图算法库实现&#xff0c;这个库不仅包含常用的图算法&#xff0c;还包含简单的绘图功能&#xff0c;非常适合研究分析使用。 …

【ARMv8M Cortex-M33 系列 2.1 -- Cortex-M33 使用 .hex /.srec 文件介绍】

请阅读【嵌入式开发学习必备专栏 之Cortex-M33 专栏】 文章目录 HEX 文件介绍英特尔十六进制文件格式记录类型hex 示例Cortex-M 系列hex 文件的使用 hex 文件和srec 文件生成Motorola S-Record (srec) 格式 HEX 文件介绍 .hex 文件通常用于微控制器编程&#xff0c;包括 ARM C…

TypeError: control character ‘delimiter‘ cannot be a newline (`\r` or `\n`)

报错 找到错误代码 这个错误是因为在使用 numpy.loadtxt() 函数时尝试将换行符&#xff08;\n&#xff09;作为分隔符&#xff08;delimiter&#xff09;。然而&#xff0c;换行符是用于标识文本文件中每一行的结束&#xff0c;而不是用于分隔数据字段。 解决 如果你的数据文…

c++ OpenCV4图像处理与视频分析实战教程 -> 自建代码库

OpenCV4图像处理与视频分析实战教程&#xff0c;系列视频自建代码库。C版本。 视频可B站搜索。 简介&#xff1a; 网上流传的“OpenCV4图像处理与视频分析实战”课程附带资料需要解压密码&#xff0c;无法使用&#xff0c;且原视频购买很贵&#xff08;51CTO售价119&#xff0…

在Mac上恢复SD卡数据的 6 个有效应用程序

慌&#xff01;SD卡里的照片和视频不小心删了&#xff0c;Mac设备上还恢复不了数据&#xff01; 遇到这种情况&#xff0c;你需要的是一款可靠的Mac适用的SD卡恢复软件。我们为你准备了一份最佳的SD卡恢复软件列表&#xff0c;并且还有详细的评论。另外&#xff0c;我们还会给…

用通俗易懂的方式讲解大模型:LangChain Agent 原理解析

LangChain 是一个基于 LLM&#xff08;大型语言模型&#xff09;的编程框架&#xff0c;旨在帮助开发人员使用 LLM 构建端到端的应用程序。它提供了一套工具、组件和接口&#xff0c;可以简化创建由 LLM 和聊天模型提供支持的应用程序的过程。 LangChain 由几大组件构成&#…

k8s搭建(五、k8s可视化管理工具Dashboard配置)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

【基础】【Python网络爬虫】【8.Selenium入门】selenium配置、环境安装、浏览器驱动下载(附大量案例代码)(建议收藏)

Python网络爬虫基础 Selenium 入门1. 动态网页&静态网页动态网页JavaScriptJQueryAjaxHTML 动态网页处理方法 2. Selenium 工作原理3. Selenium 配置环境安装浏览器驱动下载配置浏览器驱动 selenium 快速上手 4. Driver对象的常用方法及属性5. 元素提取6. 元素对象的方法及…

【基础】【Python网络爬虫】【5.数据解析】bs4、Xpath、Parsel模块、正则表达式(附大量案例代码)(建议收藏)

Python网络爬虫基础 数据解析1. 为何数据解析2. 常见的数据类型结构化数据半结构化数据非结构化数据 3. 爬虫项目实现步骤 数据解析模块1. Bs4环境安装bs4解析流程案例 - bs4碧血剑文本爬取 2. Xpath环境安装xpath解析的编码流程xpath表达式如何理解&#xff1f;案例 - 简历模板…