C语言实现字符串匹配KMP算法


相信很多人(包括自己)初识KMP算法的时候始终是丈二和尚摸不着头脑,要么完全不知所云,要么看不懂书上的解释,要么自己觉得好像心里了解KMP算法的意思,却说不出个究竟,所谓知其然不知其所以然是也。

字符串匹配是计算机的基本任务之一。

举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD"?

下面的的KMP算法的解释步骤

1.

首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较。因为B与A不匹配,所以搜索词后移一位。

2.

因为B与A不匹配,搜索词再往后移。

3.

就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。

4.

接着比较字符串和搜索词的下一个字符,还是相同。

5.

直到字符串有一个字符,与搜索词对应的字符不相同为止。

6.

这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。

7.

一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。

8.

怎么做到这一点呢?可以针对搜索词,算出一张《部分匹配表》(Partial Match Table)。这张表是如何产生的,后面再介绍,这里只要会用就可以了。

9.

已知空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。查表可知,最后一个匹配字符B对应的"部分匹配值"为2,因此按照下面的公式算出向后移动的位数:

  移动位数 = 已匹配的字符数 - 对应的部分匹配值

因为 6 - 2 等于4,所以将搜索词向后移动4位。

10.

因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2("AB"),对应的"部分匹配值"为0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移2位。

11.

因为空格与A不匹配,继续后移一位。

12.

逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动4位。

13.

逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 - 0,再将搜索词向后移动7位,这里就不再重复了。

14.



下面介绍《部分匹配表》是如何产生的。

首先,要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

15.

"部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。以"ABCDABD"为例,

  - "A"的前缀和后缀都为空集,共有元素的长度为0;

  - "AB"的前缀为[A],后缀为[B],共有元素的长度为0;

  - "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;

  - "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;

  - "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;

  - "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;

  - "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

16.

"部分匹配"的实质是,有时候,字符串头部和尾部会有重复。比如,"ABCDAB"之中有两个"AB",那么它的"部分匹配值"就是2("AB"的长度)。搜索词移动的时候,第一个"AB"向后移动4位(字符串长度-部分匹配值),就可以来到第二个"AB"的位置。

  接下来,就是我自己对KMP算法的实现了。

  这个算法的实现主要包括了三个方面:

  1) 求得我们用来搜索字符串的部分匹配值表

  2) 实现待搜索字符串在搜索过程中的指针的移动问题

  3) 如何定位我们搜索到的结果

  接下来我就贴上我实现的代码

  

/*
*用KMP算法实现字符串匹配搜索方法
*该程序实现的功能是搜索本目录下的所有文件的内容是否与给定的
*字符串匹配,如果匹配,则输出文件名:包含该字符串的行
*待搜索的目标串搜索指针移动位数 = 已匹配的字符数 - 对应部分匹配值
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define KEYWORD_MAX_LENGTH 100      //设定搜索串的最大长度
int kmp_table[KEYWORD_MAX_LENGTH];  //为搜索串建立kmp表
char prefix_stack[KEYWORD_MAX_LENGTH]; //前缀表达式栈
char suffix_stack[KEYWORD_MAX_LENGTH]; //后缀表达式栈
int keyword_length = 0;  //搜索串的长度
int record_position[KEYWORD_MAX_LENGTH]; //记录与关键字串匹配源串中的位置
/*
*GetMatchValue:获得字符串src的部分匹配值
*/
int GetMatchValue(char *src)
{int value = 0;int src_len = strlen(src);char *begin = src;    //初始化指向字符串第一个字符char *end = src + (src_len - 1);  //初始化指向字符串最后一个字符int i = 0;for(i=0;i<(src_len-1);i++){prefix_stack[i] = *begin;suffix_stack[i] = *end;begin++;end--;}char *p = prefix_stack;char *q = suffix_stack + (src_len - 2);  //指向栈中最后一个元素int flag = 0;   //用一个标志位来确定后缀栈中到最后一个元素都与前缀栈中的符号匹配while(q >= suffix_stack){if(*p == *q){value++;p++;flag=1;}else {flag = 0;}q--;}if(flag == 0) value = 0;return value;
}
/*
*创建搜索字符串的KMP表
*/
int Create_KMP_Table(char *str,int *table)
{int i;char *dst;keyword_length = strlen(str);for(i=0;i<keyword_length;i++){if(i == 0) {table[i] = 0;   //第一个字符无前缀和后缀,所以为0}else {dst = (char*)malloc((i+2));if(dst == NULL){printf("malloc space error!\n");return EXIT_FAILURE;}strncpy(dst,str,(i+1));   //匹配str的前(i+1)个字符dst[i+1] = '\0';    //注意字符串要以'/0'结尾table[i] = GetMatchValue(dst); free((void*)dst);    }}return EXIT_SUCCESS;
}
//打印搜索字符串对应的KMP表
void Table_Print(char *str,int *table)
{int i;char c = *str;while(c != '\0'){printf("%-4c",c);        //左对齐输出搜索字符串中的字符c = *++str;}printf("\n");for(i=0;i<keyword_length;i++){printf("%-4d",table[i]); //左对齐输出每个字符对应的部分匹配值}printf("\n");
}
//在目标串dst_str中搜索关键子串search_str,打印出关键字串的位置信息,返回与关键字串匹配的数目
int Search_Keyword(char *dst_str,char *search_str)
{char *p = dst_str;char *q = search_str;char *temp;
    //创建关键字串的KMP表    Create_KMP_Table(search_str,kmp_table);int count = 0;  //记录现在已经匹配的数目int k = 0;     //记录与关键字串匹配的字串的数目int move = 0;  //当字符串不匹配时,搜索指针移动的位数    
    while(*p != '\0')   //直到搜索到目标串的最后一个字符为止{temp = p;while(*q != '\0'){if(*q == *temp){count++;temp++;q++;}else break;}if(count == 0)p++;else {if(count == keyword_length){record_position[k++] = (temp-dst_str)-(keyword_length);}move = count - kmp_table[count-1];p += move;}
        count = 0;q = search_str;}return k;
}
int main(int argc,char **argv)
{char *search_str = argv[1];//char dst_str[] = "hello woshijpf woshijpf woshij woshijp woshijpf";char dst_str[] = "BBC ABCDAB ABCDABCDABDE";printf("Please input serach string and dst_string\n");if(search_str == NULL){printf("Please input search string\n");return EXIT_FAILURE;}
    if(dst_str == NULL){printf("Please input dst_string\n");return EXIT_FAILURE;}int result = Search_Keyword(dst_str,search_str);  //放回搜索到的结果的数目Table_Print(search_str,kmp_table);printf("%s\n",dst_str);         //输出待搜索的目标串if(result == 0){printf("Sorry!Don't find the string %s\n",search_str);return EXIT_SUCCESS;}else {int i,j,num;int before = 0;for(i=0;i<result;i++){num = record_position[i] - before;    //打印搜索串在目标串中的位置before = record_position[i]+1;for(j=1;j<=num;j++)printf(" ");printf("*");}printf("\n");}return EXIT_SUCCESS;
}

  测试的结果:

  

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

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

相关文章

Linux问题分析或解决_samba无法连接

1. windows设置方面问题 问题&#xff1a;window能连接部分服务器的samba共享&#xff0c;一部分无法连接。报错如截图。 解决&#xff1a;前提---其他人连接都没有问题&#xff0c;发现有问题的连接服务器的电脑是win10&#xff0c;而win10可以连接到的服务器系统比较新&#…

Drools 7支持DMN(决策模型和表示法)

决策模型和表示法&#xff08;DMN&#xff09;规范是OMG&#xff08;对象管理小组&#xff09;提出的相对较新的标准&#xff0c;旨在为业务规则和业务决策做些什么。 BPMN&#xff08;它的兄弟规范&#xff09;用于业务流程&#xff1a;标准化表示法和执行语义&#xff0c;以…

Ubuntu系统下Python虚拟环境构建详解

在编程开发中&#xff0c;我们经常会利用不同版本的协助软件包&#xff0c;这样就导致一些软件不能兼容&#xff0c;为了解决这个问题呢&#xff0c;我们在儿引进Python虚拟环境&#xff0c;我们安装好虚拟环境之后&#xff0c;进一步激活它&#xff0c;然后在虚拟环境中运行不…

字符串匹配算法

1. 朴素算法 朴素算法是最简单的字符串匹配算法&#xff0c;也是人们接触得最多的字符串匹配算法。 2. Rabin-Karp算法 一个时间复杂度为O(&#xff08;N-M1)*M)的字符串匹配算法&#xff0c;即Rabin-Karp算法。Rabin-Karp算法的预处理时间是O(m)&#xff0c; 匹配时间OO(&…

SolrCloud集群配置

前提&#xff1a; 1&#xff0c;已经做好zookeeper集群或伪集群配置. 2&#xff0c;已将solr部署到tomcat中 接下来&#xff0c;我们将zookeeper与tomcat进行关联 1 vim tomcat/bin/catalina.sh tomcat1的bin目录下catalina.sh文件在第二行加入 1 JAVA_OPTS"-Dbootstrap_c…

Ubuntu18.04 台式电脑插入耳机没有声音解决办法

最近换位ubnutu18.04后发现电脑戴耳机没有声音网上查了一下解决办法如下&#xff1a; 1、打开命令行&#xff0c;输入&#xff1a;sudo apt-get install pavucontrol 2、接着再命令行中输入: pavucontrol 在上面点击向右按钮&#xff0c;然后会出现configuration&#xff0…

第2章 网页基础知识

HTTP 基础术语HTTP 请求过程HTTP Headers 信息网页的组成网页的结构HTML节点树CSS 选择器爬虫的基本原理HTTP CookiesHTTP 代理转载于:https://www.cnblogs.com/pzk7788/p/10512338.html

适用于无服务器Java开发人员的AWS Lambda:它为您带来了什么?

无服务器计算如何帮助您的生产基础架构&#xff1f; 在过去的几年中&#xff0c;无服务器计算架构一直受到关注&#xff0c;因为它专注于应用程序的主要组件之一&#xff1a;服务器。 这种体系结构采用了不同的方法。 在下面的文章中&#xff0c;我们将解释无服务器的含义&am…

Python的sys.stdout、sys.stdin重定向

Python的sys.stdout、sys.stdin重定向 转自&#xff1a;http://www.cnblogs.com/turtle-fly/p/3280519.html 本文环境&#xff1a;Python 2.7 使用 print obj 而非 print(obj) 一些背景 sys.stdout 与 print 当我们在 Python 中打印对象调用 print obj 时候&#xff0c;事实上…

Win10+Ubuntu16.04/Ubuntu18.04双系统安装教程

最近因为开发需要安装Linux系统&#xff0c;因为安装好几次Ubuntu18.04失败&#xff0c;退而安装Ubuntu16.04 安装也失败好几次&#xff0c;在不断尝试下终于解决&#xff0c;后来思考一下觉得Ubuntu 16.04/18.04安装失败原因一致,先进行分享。 先把我遇到的问题给大家看看如下…

获取XML的文件信息

1 /**2 * 获取XML文件的信息3 */4 import java.io.IOException;5 import javax.xml.parsers.DocumentBuilder;6 import javax.xml.parsers.DocumentBuilderFactory;7 import javax.xml.parsers.ParserConfigurationException;8 import org.w3c.dom.Document;9 import org.w3c…

python 中的三元表达式(三目运算符)

python中的三目运算符不像其他语言 其他的一般都是 判定条件?为真时的结果:为假时的结果 如 result5>3?1:0 这个输出1&#xff0c;但没有什么意义&#xff0c;仅仅是一个例子。 而在python中的格式为 为真时的结果 if 判定条件 else 为假时的结果 还是上面的例子 1 if 5…

Linux 命令简单介绍第一课笔记

第一&#xff1a; 相对路径和绝对路径 相对路径:从当前路径开始进入blog文件夹 cd blog绝对路径&#xff1a;从跟目录开始进入blog文件夹 cd /home/yq/Desktop/blog 全称&#xff1a;根目录下home文件下的yq文件下的Desktop文件下的blog文件夹cd ./blog&#xff1a;进入当前…

我是如何转行 AI 并且实现薪资翻倍的

大家好啊&#xff0c;我是董董灿。 熟悉我的小伙伴都知道&#xff0c;我之前在北京某211大学&#xff0c;本硕读了7年的机械专业&#xff0c;后来硕士毕业后&#xff0c;果断转行去做了嵌入式开发&#xff0c;随后瞅准了 AI 爆发的时机果断转行去做了AI。 这段经历已经过去了…

16_1

从16年开始向前&#xff0c;就变得简单了 一、问题 一&#xff0c;给定一个数n&#xff0c;将这个数的各位顺序颠倒&#xff0c;称为逆序数m。 例如1234的逆序数是4321 如果m是n的整数倍&#xff0c;那么输出n*km&#xff0c;例如&#xff1a; 输入&#xff1a; 1089 输出&…

免费网络研讨会:调试生产中Java的新方法

什么是最有用的Java生产调试技术&#xff1f; 您永远不知道将新代码部署到生产中时会发生什么。 曾经很好的工作代码可能会变成有问题的应用程序&#xff0c;无法按预期工作。 这就是为什么在生产中进行调试是了解应用程序在现实生活中的行为而不是您如何思考其行为的关键要素。…

你必须了解的session的本质

有一点我们必须承认&#xff0c;大多数web应用程序都离不开session的使用。这篇文章将会结合PHP以及http协议来分析如何建立一个安全的会话管理机制。我们先简单的了解一些http的知识&#xff0c;从而理解该协议的无状态特性。然后&#xff0c;学习一些关于cookie的基本操作。最…

Linux 命令简单介绍第二课笔记

第一&#xff1a;touch touch 1.txt 创建文件第二&#xff1a;ls ls 用来查看当前文件中的内容&#xff0c;其中加上不同参数-a,-h,-l可以有不同的显示&#xff0c;同事也可以将一个具体内容从定向到一个TXT文件中&#xff1a;具体如下 第三&#xff1a;grep搜索 grep:文本搜索…

人工智能实战_第一次作业_杨佳宁_16141032

项目内容这个作业属于哪个课程班级博客这个作业的要求在哪里作业要求我在这个课程的目标是对于人工智能有一定的了解这个作业在哪个具体方面帮助我实现目标能够有平台支持我对于人工智能更加深入的了解与交流作业正文见下其他参考文献无具体作业1、描述你在这门课想要达到的具体…

Linux压缩和解压缩命令汇总(tar命令汇总)

linux 中压缩文件非常常见&#xff0c;以下为本人对Linux压缩和解压缩命令进行了简单汇总&#xff0c;具体如下&#xff1a; 第一&#xff1a;打包解包 打包命令&#xff08;注意打包文件大小并没有压缩&#xff09; tar -cvf xxx.tar 要打包的文件解包命令 tar -xvf xxx.…