串这个概念对于我们学到现在的水平来说应该是经历颇丰了,因为在C语言中我们所用到的“串”知识是在字符串那里,有了这个概念,我们再去学习串就相对而言轻松多了。

那么,现在来介绍一下字符串的基本知识点吧:

首先,所谓的串:

1.都由ASCII码组成;

2.长度基本没有要求

串的表示方式:

1.顺序存储结构——数组

2.非线性存储结构——链表

那么,我们在C语言中对于串的处理,一般无非是以下几种:

1.初始化“串”;

2.销毁“串”;

3.获取“串”长度;

4.插入单字符;

5.删除单字符;

6.定位单字符;

7.更改单字符;

8.取子串;

9.分割单字符;

10.合并单字符;

11.串匹配;

12.替换

这里和我们之前博文中对于 链表 和 表达式 的处理思想近乎相同,这里就不进行枯燥的复述了

那么,在这篇博文中,本人主要讲解一个算法来解决字符串匹配问题——KMP算法

首先,本人来解释一下什么是字符串匹配问题:

字符串匹配问题:

在一个源字符串中,查找一个目标字符串(子字符串)的第一次出现位置。

本人现在这里来阐释一下算法的基本思想:

根据给出的字串,得到一个数组,来储存当子串中的每一个元素的适配个数,然后根据这个数组中的值,遍历并比较源串和子串,当遇到不匹配的位置,读取该位置的适配字符数,将该数作为再次比较时源串的开始下表下标,因为我们这些。

那么,什么是适配呢?

适配就是指:与该字符紧挨着的前缀的字符串的部分长度,与从该字符串刚开始开始比较,长度相等、内容也完全相等的长度。

假设现在有一个字符串:

annbcdanacadsannannabnna

现在要求查找如下字串:

annacanna

那么,本人根据子串的信息来得出一个数组:

下标 字符 适配字符数 适配串
0 a 0
1 n 0
2 n 0
3 a 0
4 c 1 a
5 a 0
6 n 0
7 n 1 a
8 a 0
next[] = {0, 0, 0, 0, 1, 0, 0. 0}

这个例子其实算是比较简单的,还不能完全体现我们要初始化这个数组的原理,现在本人来给出一个比较复杂的例子:

源串:aabaabaabaabaabaaaabaabaab

子串:aabaabaaaabaa

那么,本人根据子串的信息来得出一个数组:

下标 字符 适配字符数 适配串
0 a 0
1 a 0
2 b 1 a
3 a 0
4 a 1 a
5 b 2 aa
6 a 3 aab
7 a 4 aaba
8 a 5 aabaa
9 a 2 aa
10 b 2 aa
11 a 3 aab
12 a 4 aaba
next[] = {0, 0, 1, 0, 1, 2, 3, 4, 5, 2, 2, 3, 4}

现在,本人来通过两张图来展示下这个数组的作用:



没错,这个数组就是当子串和源串相比失配时应该移动的长度。

那么,了解了上述的算法的大致流程,我们现在就来用代码来实现一下:

首先,还是先来编写本人一贯的头文件:

mec.h:

#ifndef _MEC_H_
#define _MEC_H_ typedef unsigned char boolean;
#define TRUE 1
#define FALSE 0
#define NOT_FOUND -1 #endif

KMPSearch.c:

#include <stdio.h>
#include <malloc.h>
#include <string.h> #include "mec.h" void getNext(const char *str, int *next);
int KMPMatch(const char *str, const char *sub); /*通过KMP算法查找字串位置 函数*/
int KMPMatch(const char *str, const char *sub) { //因为我们只是要查找位置,所以不能对 源串 以及 子串 进行更改
int *next;
int strLen; //用于存储 源串 长度
int subLen; //用于存储 子串 长度
int i = 0;
int j = 0; if (NULL == str || NULL == sub
|| (subLen = strlen(sub)) > (strLen = strlen(str))) {
return NOT_FOUND;
} next = (int *) calloc(sizeof(int), subLen); //我们将next数组的长度定为字串长度为了之后直接跳过不会适配的长度
getNext(sub, next); while (str[i] && sub[j]) {
if (str[i] != sub[j]) {
if (j == 0) {
++i;
} else {
j = next[j];
//因为目标串失配点的前面的部分适配子串 和 目标串开头的部分子串内容是一样的,所以不用考虑开头那部分子串
//所以我们跳过比较这段字符串,从后面的子串开始比较,
}
} else {
++i;
++j;
}
if (sub[j] == 0) { //当我们比较到字串的下标为j时,发现子串被遍历完了,也就意味着这时子串在源串中的位置找到了
free(next);
return i - j; //因为字串长度是j,所以子串的第一个字符在源串中所对应的下标应改为当前下标(即i)- j
}
}
free(next); return NOT_FOUND;
} /*产生适配数组 函数*/
void getNext(const char *str, int *next) {
int i = 2;
int j = 0;
boolean isSame; if (strlen(str) < 3) { //因为我们之后从源串的第三个单元找起,所以长度不能小于3
return;
} while (str[i]) { //遍历 源串,查找适配点
isSame = str[i-1] == str[j];
if (isSame || j == 0) {
next[i++] = !isSame ? 0 : ++j;
} else {
j = next[j];
}
}
} int main() {
char str[80];
char sub[80];
int index; printf("请输入源串:");
gets(str);
printf("请输入子串:");
gets(sub); index = KMPMatch(str, sub); if (NOT_FOUND == index) {
printf("未找到!\n");
} else {
printf("在第%d个位置!\n", index+1); //因为数组的下标是从0开始,所以我们在表示时,要给 下标+1
} return 0;
}

下面,我们来看一下运行结果:

可以看到,查找结果是正确的!

那么,现在本人再给出一对不存在包含关系的源串与子串,让我们再来看看查找结果:

我们能够清晰地看到,运行结果都是正确的!

串匹配问题 (KMP算法) 详解的更多相关文章

  1. 字符串匹配的KMP算法详解及C#实现

    字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD" ...

  2. KMP算法详解&&P3375 【模板】KMP字符串匹配题解

    KMP算法详解: KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt(雾)提出的. 对于字符串匹配问题(such as 问你在abababb中有多少个 ...

  3. kmp算法详解

    转自:http://blog.csdn.net/ddupd/article/details/19899263 KMP算法详解 KMP算法简介: KMP算法是一种高效的字符串匹配算法,关于字符串匹配最简 ...

  4. [转] KMP算法详解

    转载自:http://www.matrix67.com/blog/archives/115 KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段.    我们这里说的K ...

  5. KMP算法详解(转自中学生OI写的。。ORZ!)

    KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段. 我们这里说的KMP不是拿来放电影的(虽然我很喜欢这个软件),而是一种算法.KMP算法是拿来处理字符串匹配的.换句 ...

  6. 数据结构4.3_字符串模式匹配——KMP算法详解

    next数组表示字符串前后缀匹配的最大长度.是KMP算法的精髓所在.可以起到决定模式字符串右移多少长度以达到跳跃式匹配的高效模式. 以下是对next数组的解释: 如何求next数组: 相关链接:按顺序 ...

  7. 算法进阶面试题01——KMP算法详解、输出含两次原子串的最短串、判断T1是否包含T2子树、Manacher算法详解、使字符串成为最短回文串

    1.KMP算法详解与应用 子序列:可以连续可以不连续. 子数组/串:要连续 暴力方法:逐个位置比对. KMP:让前面的,指导后面. 概念建设: d的最长前缀与最长后缀的匹配长度为3.(前缀不能到最后一 ...

  8. 字符串匹配KMP算法详解

    1. 引言 以前看过很多次KMP算法,一直觉得很有用,但都没有搞明白,一方面是网上很少有比较详细的通俗易懂的讲解,另一方面也怪自己没有沉下心来研究.最近在leetcode上又遇见字符串匹配的题目,以此 ...

  9. 字符串匹配的Boyer-Moore算法 详解 加 C# 实现

    上一篇文章,我介绍了KMP算法. 但是,它并不是效率最高的算法,实际采用并不多.各种文本编辑器的"查找"功能(Ctrl+F),大多采用Boyer-Moore算法. Boyer-Mo ...

随机推荐

  1. 使用wrd2vec构建推荐系统

    概览 完整的代码可以从这里下载: https://github.com/prateekjoshi565/recommendation_system/blob/master/recommender_2. ...

  2. C# 便捷实现可迭代对象间的赋值

    目录 都是迭代,为啥我一定要用foreach 如果换成是字典呢? 关于 foreach 都是迭代,为啥我一定要用foreach ​ 问题起源于本人的一个练手的扑克牌程序:洗完牌之后要发给场上的三人. ...

  3. 8.MSFvenom

    Meterpreter 01 Meterpreter API调用 Meterpreter提供了多种APl调用,在编写自己的脚本时可以使用这些API来提供额外功能或定制功能. 关于ruby的更多信息,请 ...

  4. 实验十三 MySQL多用户事务管理

    实验十三 MySQL多用户事务管理 一.  实验内容: 1. 事务机制的使用 2. 锁机制的使用 二.  实验项目:员工管理数据库 用于企业管理的员工管理数据库,数据库名为YGGL中,YGGL数据库中 ...

  5. linux下zip/unzip详解

    linux下zip_unzip详解 命令列表:zip    -q (quiet)    -r (recursive)    -0(level0-level9)    -e (encrypt)    - ...

  6. vue+springboot后台实现页面按钮权限

    思路 1.用户跟角色关联 2.角色跟菜单关联 3.菜单跟菜单下的按钮关联 4.后端返回每个菜单下的按钮,前端通过自定义事件,在每个按钮上加上相应的事件 打字麻烦,还是看图吧! 建立btn.js 然后在 ...

  7. 1048 Find Coins (25分)

    Eva loves to collect coins from all over the universe, including some other planets like Mars. One d ...

  8. 部署mysql主从同步

                                                 mysql-day06        部署mysql主从同步 案例拓扑 • 一主.一从 – 单向复制时,建议将 ...

  9. Android如何快速打出100个渠道apk

    测试1分钟900多个包 关键思路就是读文件,如图: Python快速打包脚本: #!/usr/bin/env python import zipfile prefix = 'channel_' cha ...

  10. json.dumps() 和 json.loads()

    转载: https://blog.csdn.net/qinglingls/article/details/96476368