字符串匹配算法

1. 朴素算法

朴素算法是最简单的字符串匹配算法,也是人们接触得最多的字符串匹配算法。

2. Rabin-Karp算法

一个时间复杂度为O((N-M+1)*M)的字符串匹配算法,即Rabin-Karp算法。Rabin-Karp算法的预处理时间是O(m), 匹配时间OO((N-M+1)*M),既然与朴素算法的匹配时间一样,而且还多了一些预处理时间,那为什么我们 还要学习这个算法呢?

虽然Rain-Karp在最坏的情况下与朴素的世间复杂度一样,但是实际应用中往往比朴素算法快很多。而且该算法的 期望匹配时间是O(N+M)(参照《算法导论》)。

在朴素算法中,我们需要挨个比较所有字符,才知道目标字符串中是否包含子串。那么, 是否有别的方法可以用来判断目标字符串是否包含子串呢?

答案是肯定的,确实存在一种更快的方法。为了避免挨个字符对目标字符串和子串进行比较, 我们可以尝试一次性判断两者是否相等。因此,我们需要一个好的哈希函数(hash function)。 通过哈希函数,我们可以算出子串的哈希值,然后将它和目标字符串中的子串的哈希值进行比较。 这个新方法在速度上比暴力法有显著提升。

Rabin-Karp算法的思想:

  1. 假设子串的长度为M,目标字符串的长度为N
  2. 计算子串的hash值
  3. 计算目标字符串中每个长度为M的子串的hash值(共需要计算N-M+1次)
  4. 比较hash值
  5. 如果hash值不同,字符串必然不匹配,如果hash值相同,还需要使用朴素算法再次判断

为了快速的计算出目标字符串中每一个子串的hash值,Rabin-Karp算法并不是对目标字符串的 每一个长度为M的子串都重新计算hash值,而是在前几个字串的基础之上, 计算下一个子串的 hash值,这就加快了hash之的计算速度,将朴素算法中的内循环的世间复杂度从O(M)将到了O(1)。

关于hash函数的详细内容,可以参考这里或者《算法导论》。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include<stdio.h>
#include<string.h>
// d is the number of characters in input alphabet
#define d 256
/*  pat  -> pattern
    txt  -> text
    q    -> A prime number
*/
void search(char *pat, char *txt, int q)
{
    int M = strlen(pat);
    int N = strlen(txt);
    int i, j;
    int p = 0;  // hash value for pattern
    int t = 0; // hash value for txt
    int h = 1;
    // The value of h would be "pow(d, M-1)%q"
    for (i = 0; i < M-1; i++)
        h = (h*d)%q;
    // Calculate the hash value of pattern and first window of text
    for (i = 0; i < M; i++)
    {
        p = (d*p + pat[i])%q;
        t = (d*t + txt[i])%q;
    }
    // Slide the pattern over text one by one
    for (i = 0; i <= N - M; i++)
    {
        // Chaeck the hash values of current window of text and pattern
        // If the hash values match then only check for characters on by one
        if ( p == t )
        {
            /* Check for characters one by one */
            for (j = 0; j < M; j++)
            {
                if (txt[i+j] != pat[j])
                    break;
            }
            if (j == M)  // if p == t and pat[0...M-1] = txt[i, i+1, ...i+M-1]
            {
                printf("Pattern found at index %d \n", i);
            }
        }
        // Calulate hash value for next window of text: Remove leading digit,
        // add trailing digit          
        if ( i < N-M )
        {
            t = (d*(t - txt[i]*h) + txt[i+M])%q;
            // We might get negative value of t, converting it to positive
            if(t < 0)
              t = (t + q);
        }
    }
}
/* Driver program to test above function */
int main()
{
    char *txt = "GEEKS FOR GEEKS";
    char *pat = "GEEK";
    int q = 101;  // A prime number
    search(pat, txt, q);
    getchar();
    return 0;
}

  

3. KMP算法

 KMP算法之所以叫做KMP算法是因为这个算法是由三个人共同提出来的,就取三个人名字的首字母作为该算法的名字。其实KMP算法与BF算法的区别就在于KMP算法巧妙的消除了指针i的回溯问题,只需确定下次匹配j的位置即可,使得问题的复杂度由O(mn)下降到O(m+n)。

  在KMP算法中,为了确定在匹配不成功时,下次匹配时j的位置,引入了next[]数组,next[j]的值表示P[0...j-1]中最长后缀的长度等于相同字符序列的前缀。

  对于next[]数组的定义如下:

 1) next[j] = -1  j = 0

 2) next[j] = max(k): 0<k<j   P[0...k-1]=P[j-k,j-1]

 3) next[j] = 0  其他

 如:

 P      a    b   a    b   a

 j      0    1   2    3   4

 next    -1   0   0    1   2

 即next[j]=k>0时,表示P[0...k-1]=P[j-k,j-1]

 因此KMP算法的思想就是:在匹配过程称,若发生不匹配的情况,如果next[j]>=0,则目标串的指针i不变,将模式串的指针j移动到next[j]的位置继续进行匹配;若next[j]=-1,则将i右移1位,并将j置0,继续进行比较。

 

 

复制代码
#include<stdio.h>
#include<string.h>
void getNext(char *p, int *next);int KMPMatch(char *s ,char *p)
{int next[100] = {0};int M = strlen(s);int N = strlen(p);getNext(p,next);int i = 0, j = 0;while( i < M ) {if(next[j] == -1 || s[i] == p[j]){i++;j++;}else{j = next[j];}if(j == N){return i - N;}}return -1;}void getNext(char *p , int *next)
{int j , k ;next[0] = -1;j = 0;k = -1;while(j < strlen(p)){if(k == -1 || p[j] == p[k]){k++;j++;next[j] = k;}else{k = next[k];}}
}int main()
{char *s = "lovely puppy , jianghaha";char *p = "jiang";printf( "匹配位置:%d\n" , KMPMatch(s , p)) ;return 0;
}
复制代码

因此KMP算法的关键在于求算next[]数组的值,即求算模式串每个位置处的最长后缀与前缀相同的长度, 而求算next[]数组的值有两种思路,第一种思路是用递推的思想去求算,还有一种就是直接去求解。 

1.按照递推的思想:

   根据定义next[0]=-1,假设next[j]=k, 即P[0...k-1]==P[j-k,j-1]

   1)若P[j]==P[k],则有P[0..k]==P[j-k,j],很显然,next[j+1]=next[j]+1=k+1;

   2)若P[j]!=P[k],则可以把其看做模式匹配的问题,即匹配失败的时候,k值如何移动,显然k=next[k]。

 

4. Boyer-Moore算法

待补充。http://blog.jobbole.com/52830/

5. Sunday算法

http://blog.163.com/yangfan876@126/blog/static/80612456201342205056344

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

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

相关文章

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.…

MFC程序打开控制台

转自&#xff1a;http://blog.csdn.NET/acaiacc/article/details/5543669 利用AllocConsole() 在你的MFC程序中添加这些代码&#xff0c;可以获得控制台的输入&#xff0c;你试试 AllocConsole(); freopen("CONOUT$","wt",stdout); freopen("…

jmeter异步请求测试_使用JMeter对异步HTTP / REST服务进行压力/负载测试

jmeter异步请求测试尽管我一直在使用JMeter进行Web应用程序的压力测试和负载测试好几次&#xff0c;但我们还是花了一些时间才弄清楚如何使用该工具测试基于异步HTTP / REST的服务。 对我们来说&#xff0c;我是一个程序员的同伴– Holger Staudacher &#xff0c;我很荣幸能与…

Linux 中vim编辑器学习笔记

vim是Linux是非常常用的编辑器&#xff0c;也是编程开发中的神器之一。接下来&#xff0c;是我学习vim编辑器的一些笔记&#xff0c;总结如下&#xff1a; 首先&#xff1a;我们接触的vim编辑器一共有三种模式&#xff0c;分别为命令模式、编辑模式、末行模式。 具体架构如下 …

Python 面向对象(初级篇)

概述面向过程&#xff1a;根据业务逻辑从上到下写垒代码函数式&#xff1a;将某功能代码封装到函数中&#xff0c;日后便无需重复编写&#xff0c;仅调用函数即可面向对象&#xff1a;对函数进行分类和封装&#xff0c;让开发“更快更好更强...”面向过程编程最易被初学者接受&…