单模式串匹配----浅谈kmp算法

模式串匹配,顾名思义,就是看一个串是否在另一个串中出现,出现了几次,在哪个位置出现;

p.s.  模式串是前者,并且,我们称后一个 (也就是被匹配的串)为文本串;

  在这篇博客的代码里,s1均为文本串,s2均为模式串;

  一般地,文本串长度不小于匹配串;(否则无意义)

很显然可以得到一个暴力的做法 :

for i : 1~lenth_of_s1 {//枚举匹配串在文本串中的开始位置
    for j : 1~lenth_of_s2
        if(s2[j]!=s1[i+j-1]) break;
    if j>lenth_of_s2  //在循环结束前没有break
        output : i
}

时间复杂度:O ( TLE )  ------     O (N+M) ~ O(N*M)

所以需要一个更优的算法;

可以发现,在枚举匹配串在文本串中的开始位置时,有很多步骤是无效的,因为匹配串的第一个字符 很有可能和当前枚举到的开始位置 不同;

所以可以优化这个过程,每次改变开始位置时,直接移动到下一个和匹配串第一个字符相同的位置 (类似于链表;

int next[N], pos=-1;
char head = s2[1];

for i : lenth_of_s1~1
    if s1[i]==head {
        next[i] = pos;
        pos = i;
    }
next[0] = pos;

for i = next[0] ; i != -1 ; i = next[i] {
    for j : 1~lenth_of_s2
        if(s2[j]!=s1[i+j-1]) break;
    if j>lenth_of_s2  //在循环结束前没有break
        output : i
}

这个做法看起来很强,实际上很容易被卡成O (n^2);

比如说 :s1 :sssssssssssssa, s2 : sssb;

由于并没有利用所有已经匹配过的部分,所以仍然会T;

于是,就有了KMP算法。

p.s.  i表示当前在文本串中枚举到的位置,j表示模式串中的;

在s1[ i ] != s2 [ j ]时,将 j 移动到一个在 j 之前的位置k 使得 s2[ 1 ]~s2[ k ] 与 s2[ j-k+1 ]~s2[ j ]完全相同,那么时间复杂度就是O (N+M) 的了;

p.s.  因为 i , j 两个指针最多移动N+M次;

给一个写模板的链接 :https://www.luogu.org/problemnew/show/P3375

贴代码 :

// luogu-judger-enable-o2
// 15owzLy1
//luogu3375_kmp.cpp
//2018 10 02      17:27:50
#include <cstdio>
#include <cstring>
typedef long long ll;
typedef double db;
using namespace std;

const int N = 1000005;
int next[N], la, lb;
char a[N], b[N];

template<typename T>inline void read(T &x_) {
    x_=0;bool f_=0;char c_=getchar();
    while(c_<‘0‘||c_>‘9‘){f_|=(c_==‘-‘);c_=getchar();}
    while(c_>=‘0‘&&c_<=‘9‘){x_=(x_<<1)+(x_<<3)+(c_^48);c_=getchar();}
    x_=f_?-x_:x_;
}

inline void get_next() {
    int j=0;
    for(int i=2;i<=lb;i++) {
        while(j&&b[j+1]!=b[i]) j=next[j];
        if(b[j+1]==b[i]) ++j;
        next[i]=j;
    }
}

inline void kmp() {
    int j=0;
    for(int i=1;i<=la;i++) {
        while(j&&a[i]!=b[j+1]) j=next[j];
        if(b[j+1]==a[i]) ++j;
        if(j==lb)
            printf("%d\n", i-j+1);
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("luogu3375_kmp.in","r",stdin);
    freopen("luogu3375_kmp.out","w",stdout);
#endif
    scanf("\n%s%s", a+1, b+1); la=strlen(a+1), lb=strlen(b+1);
    get_next();
    kmp();
    for(int i=1;i<=lb;i++) printf("%d ", next[i]);
    puts("");
    return 0;
}



原文地址:https://www.cnblogs.com/15owzLy1-yiylcy/p/9740396.html

时间: 2024-10-17 14:27:40

单模式串匹配----浅谈kmp算法的相关文章

浅谈KMP算法及其next[]数组

KMP算法是众多优秀的模式串匹配算法中较早诞生的一个,也是相对最为人所知的一个. 算法实现简单,运行效率高,时间复杂度为O(n+m)(n和m分别为目标串和模式串的长度),比蛮力算法的O(nm)快了许多. 理解KMP算法,关键是理解其中的精髓——next[]数组. (统一起见,下文将目标字符串记作obj,将模式字符串记作pattern,这与后面的程序代码是一致的) 我们给一个字符串S定义一个next值,记作next(S),next(S)=n表示: (1)S的前n个字符构成的前缀,和后n个字符的后缀

模式字符串匹配问题(KMP算法)

这两天又看了一遍<算法导论>上面的字符串匹配那一节,下面是实现的几个程序,可能有错误,仅供参考和交流. 关于详细的讲解,网上有很多,大多数算法及数据结构书中都应该有涉及,由于时间限制,在这就不重复了. 需要说明的是: stra:主串,及需要从中寻找模式串的字符串 strb:模式串 <算法导论>上面包括严蔚敏老师<数据结构>,字符串下表是按从1开始,并且<数据结构>一书中貌似吧字符串的第一个字符用来储存字符串长度.这里我改成了0. maxlen :字符串的最长

【ZOJ】3785 What day is that day? ——浅谈KMP应用之ACM竞赛中的暴力打表找规律

首先声明一下,这里的规律指的是循环,即找到最小循环周期.这么一说大家心里肯定有数了吧,“不就是next数组性质的应用嘛”. 先来看一道题 ZOJ 3785 What day is that day? Time Limit: 2 Seconds      Memory Limit: 65536 KB It's Saturday today, what day is it after 11 + 22 + 33 + ... + NN days? Input There are multiple tes

hdu1875浅谈prim算法的朴素实现

阅读原题 题目大意 给你几个(<=100)小岛的坐标,然后你把所有的岛都修上桥连接起来,求最小花费,还有个附加的限制:只有岛之间的距离大于等于10,或小于等于1000时才能修桥. 大概是因为十米以内不用建桥,千米以上无法建桥.哈哈,说着玩的. 很明显这是一道MST(最小生成树)的题目,貌似也有人用并查集AC过. 最小生成树算法 概述 最小生成树的常用算法有两个kruskal和prim算法.两者都是不停地执行归并操作,然而一言以蔽之,两者的不同之处在于:kruskal----归并边:prim---

浅谈分词算法(2)基于词典的分词方法

[TOC] 前言 在浅谈分词算法(1)分词中的基本问题中我们探讨了分词中的基本问题,也提到了基于词典的分词方法.基于词典的分词方法是一种比较传统的方式,这类分词方法有很多,如:正向最大匹配(forward maximum matching method, FMM).逆向最大匹配(backward maximum matching method,BMM).双向扫描法.逐词遍历法.N-最短路径方法以及基于词的n-gram语法模型的分词方法等等.对于这类方法,词典的整理选择在其中占到了很重要的作用,本

浅谈分词算法(1)分词中的基本问题

[TOC] 前言 分词或说切词是自然语言处理中一个经典且基础的问题,在平时的工作中也反复的接触到分词问题,用到了不同的模型,不同的方法应用在各个领域中,所以想对分词问题做一个系统的梳理.大多数分词问题主要是针对类似汉语.韩语.日语等,词语之间并没有天然的分割,而像英语等,句子中是带有天然的分割的.但是英语也会涉及到分词问题,比如实体识别.词性标注等内容.而本系列文章更多的是讨论汉语中的分词问题,首先我们从分词问题的基本出发,之后从传统的词典分词到将分词转为序列标注问题的分词,以及最新的结合深度学

浅谈欧洲算法——模拟退火

初听说退火这个名词感觉就很(zhuang)帅(A__CDEFG...) 直到学了退火之后,我才发现: 退火不只是帅,而且非常万能 甚至比 D (大) F (法) S (师)还要万能 简直就是骗(de)分神器啊 简介 作为一个计算机算法,它竟然在百度上有物理词条! 当时我看了就懵了,你说计算机一个算法,跟冶炼金属有什么关系啊? 后来我看了算法的词条... 是不是更懵了... 方便大家理解(变得更懵),我搬了百度上的定义: Simulate Anneal Arithmetic (SAA,模拟退火算法

浅谈Manacher算法与扩展KMP之间的联系

首先,在谈到Manacher算法之前,我们先来看一个小问题:给定一个字符串S,求该字符串的最长回文子串的长度.对于该问题的求解,网上解法颇多,时间复杂度也不尽相同,这里列述几种常见的解法. 解法一 通过枚举S的子串,然后判断该子串是否为回文,由于S的子串个数大约为,加上每次判断需要的时间,所以总的时间复杂度为,空间复杂度为. bool check(string &S, int left, int right) { while (left < right && S[left]

浅谈字符串算法(KMP算法和Manacher算法)

[字符串算法1] 字符串Hash(优雅的暴力) [字符串算法2]Manacher算法 [字符串算法3]KMP算法 这里将讲述  字符串算法2:Manacher算法 问题:给出字符串S(限制见后)求出最大回文子串长度 Subtask1  对于10%的数据 |S|∈(0,100] Subtask2  对于30%的数据|S|∈(0,5000] Subtask3 对于100%的数据|S|∈(0,11000000] Subtask1(10pts):最朴素的暴力 枚举字符串的所有子串,判断其是否回文,时间复