• KMP算法(字符串匹配)


    遇到字符串匹配问题,一般我就只能想到O(nm)的朴素算法...
    今天有这样一种算法,使得复杂度变为O(n),
    这就是KMP(烤馍片)算法
    粘一个模板题先:
    给出两个字符串(s_1)(s_2),其中(s_2)(s_1)的子串,求出(s_2)(s_1)中所有出现的位置。
    然后本题还要求输出所有(s_2)中字符的前缀数组,
    现在留下一个疑点,前缀数组(这是啥?),先往后看
    首先确定一点,就是在进行字符串匹配时,会是拿一个字符串与另一个字符串的一部分进行比较,那么我们有以下称呼(拿本题进行举例):(s_2)叫做模式串,(s_1)叫做母串
    我们先分析朴素算法的原理与弊端:
    朴素算法是将每一个母串字符拿出比较,一旦不符合就会放弃前面匹配的所有信息,重头再来,
    然而KMP不然,KMP就是一种"失败为成功之母"的算法,每次在匹配失败时利用前面信息进行更高效的匹配,
    具体的思想需要用到这个"前缀数组"
    那么这是啥呢?
    先考虑一个问题:如何利用失败信息,
    如果匹配失败,我们会放弃当前匹配,然而这起码证明前面的匹配都是成功的,也就是说,只要我找到模式串前面的能与自己(当前匹配失败之前的一个位置)匹配上的子串,那么一定也能与前面匹配,进行再次查找,而不用进行朴素算法暴力枚举,
    前缀数组就是保存这样的指针的数组,预处理大概是这样的:

    inline void init(){
    	p[1]=0;
    	int j=0;
    	for(int i=1;i<m;i++){
    		while(j>0&&b[j+1]!=b[i+1]) j=p[j];
    		if(b[j+1]==b[i+1]) j++;
    		p[i+1]=j;
    	}
    }
    

    总体代码是这样的:

    #include<cstdio>
    #include<cstring>
    #include<string>
    using namespace std;
    char a[1000005];
    char b[1000005];
    int p[1000005];
    int n,m;
    inline void init(){
    	p[1]=0;
    	int j=0;
    	for(int i=1;i<m;i++){
    		while(j>0&&b[j+1]!=b[i+1]) j=p[j];
    		if(b[j+1]==b[i+1]) j++;
    		p[i+1]=j;
    	}
    }
    inline void find(){
    	int j=0;
    	for(int i=0;i<n;i++){
    		while(j>0&&a[i+1]!=b[j+1]) j=p[j];
    		if(b[j+1]==a[i+1]) j++;
    		if(j==m){
    			printf("%d
    ",i-m+2);
    			j=p[j];
    		}
    	}
    }
    int main(){
    	scanf("%s%s",a+1,b+1);
    	n=strlen(a+1);m=strlen(b+1);
    	init();
    	find();
    	for(int i=1;i<=m;i++)
    		printf("%d ",p[i]);
    	return 0;
    }
    

    为什么KMP主体与预处理这么像呢?
    因为预处理本身就是
    我 匹配 我自己
    最后就是输出前缀数组了

  • 相关阅读:
    JAVA网络编程-IP组播
    Centos7安装Node
    Android Studio解决support-annotations版本冲突
    Wordpresss建站笔记:英文模板出现中文如何解决?
    Win10系统下插入耳机前面板无声后面板有声的处理(二)
    webstorm编辑器html浏览器快捷浏览按键图标消失的处理
    近期的感想
    Octet string 解析
    SSH隧道:端口转发功能详解
    uint, not []uint8
  • 原文地址:https://www.cnblogs.com/648-233/p/11117987.html
Copyright © 2020-2023  润新知