洛谷-P3375 【模板】KMP字符串匹配-程序员宅基地

技术标签: 数据结构与算法  


题目

Problem Description

如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置。

为了减少骗分的情况,接下来还要输出子串的前缀数组next。

(如果你不知道这是什么意思也不要问,去百度搜[kmp算法]学习一下就知道了。)

 

Input

第一行为一个字符串,即为s1(仅包含大写字母)

第二行为一个字符串,即为s2(仅包含大写字母)

 

Output

若干行,每行包含一个整数,表示s2在s1中出现的位置

接下来1行,包括length(s2)个整数,表示前缀数组next[i]的值。

 

Sample Input

ABABABC
ABA

 

Sample Output

1
3
0 0 1

 


题解

  数据结构讲串的时候讲到了KMP算法,然后个人想写一写。同时,还有一个比KMP算法更快更简单的Sunday算法,也想写一写,然后就这么愉快的决定了。

KMP算法

  KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为KMP算法。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next[]数组,其本身包含了模式串的局部匹配信息。

  这里给出一种非常好理解的KMP算法。KMP算法的核心就是构造失败指针,即next[]数组。因此如果next[]数组的含义足够简单,KMP算法的内核就很好理解了。我们把两种next[]数组的构造样例放在下面,然后分析那个比较容易理解的。

下标i 0 1 2 3 4 5 6 7 8 9 10
t[i] a b c a b a b c a b c
next'[i] -1 0 0 0 1 2 1 2 3 4 5
next[i] 0 0 0 1 2 1 2 3 4 5 3

  第一种方法构造出的next[]数组其实是真正的KMP算法的next[]数组,但我管感觉有点弯弯绕,不太好理解……但是第二种方法就很好理解了,next[i]表示的是,以第i位结尾,长度为next[i]的字符串与字符串t的长度为next[i]的前缀相同。例如next[9]=5,则我们知道t[5-9]与t[0-4]相同。我们可以找出规律,第一种构造出的其实就是第二种构造出的右移一位并讲next[0]赋值为-1。我们只要愉快的记住第二个数组的构造方法和使用方法就可以了。构造这个数组非常简单,代码如下:

    j=0;
    for(i=1;i<lt;i++){
        while(j>0 && t[i]!=t[j])
            j=Next[j-1];
        if(t[i]==t[j])
            j++;
        Next[i]=j;
    }

  i指针指向应该比较的那一位,j指针之前这前缀中的应该比较的那一位。

  在上述代码中最重要的一句就是:

j=Next[j-1];

  即如果两个指针所指向的两位不匹配,则看看j的前一位与前缀中的前几个匹配,然后比较前缀中之后的那一个。说起来比较绕,但举个例子就很明白了。比如当比较下标i=10的时候,j=5。我们知道next[9]=5,即t[5-9]与t[0-4]相同,因此我们可以尝试着去匹配t[10]与t[5],如果匹配的话next[10]就可以愉快的等于next[9]+1。但是这是我们发现t[10]="c",t[5]="d",我们愉快的发现不匹配,怎么办呢?虽然不匹配,但是next[5-1]=next[4]=2,即t[3-4]与t[0-1]相同,此时便是j=Next[j-1]。于是我们又可以愉快的尝试着去匹配t[10]与t[2],然后发现匹配上了……然后就可以愉快的赋值了。

  得到的next[]数组使用起来也非常简单,代码如下:

   j=0;
    for(i=0;i<ls;i++){
        if(j==lt){
            cout << i-lt+1 << endl;
            j=Next[j-1];
        }
        while(j>0 && s[i]!=t[j])
            j=Next[j-1];
        if(s[i]==t[j])
            j++;
    }
    if(j==lt)
        cout << i-lt+1 << endl;

  理解方其实和上面是相似的。最后两行主要是防止最后一个字符串匹配无法输出的问题……

Sunday算法

  我们来介绍一种更神奇的算法——Sunday算法……KMP算法我在上面叨叨了半天也不一定能有人理解,但是为了数据结构考试我不得不搞清楚令人头疼的KMP算法。抛开考试不提,Sunday算法是一个又简单又高效的算法……

  Sunday算法是Daniel M.Sunday于1990年提出的字符串模式匹配。其核心思想是:在匹配过程中,模式串发现不匹配时,算法能跳过尽可能多的字符以进行下一步的匹配,从而提高了匹配效率。

  对于t,我们做一个简单而巧妙的预处理:找出t中每一种字符最后出现的位置,将其存入一个数组中。我们可以使用map容器……大概是O(m)的复杂度。构造好失败指针后,进行O(n)复杂度的比较。

  直接上例子,一边举例一边解说:

s: a b d a e c c a a b c
      i k              
t: a b c                
      j                

  整个程序是以指针k为主指针,首先k指向s串中的s[m](m为t串长度)。

  每次根据k的位置,我们将i指向k的前一个元素,j指向t,然后从后向前比较,为什么从后向前?比较坑的测评网站总是有一些s="aaaaaaaaab",t="aaab"之类的数据存在……

  如果s[i]和t[j]匹配的话,我们就将i和j前移,直到j指向t的头为止。不管s的子串和t匹不匹配,不匹配就不输出,匹配就输出。

  然后到了最重要的一步,我们要跳了。但是无论如何跳,s[k]一定要被判断是否在下一个匹配字符串中。最好的情况是t向后移直到t的头与s[k]对齐,最坏的情况是t向后移一位,这时t的尾与s[k]对齐。既然s[k]跳不过去,我们就使t能向后移动最多位,即将t中最后一次出现s[k]的位置和s[k]对齐。我们在与处理时愉快的做了这一步。结果就是 

s: a b d a e c c a a b c
        k              
t:       a b c          
                       

 

  然后再把k指向t的后一位:

s: a b d a e c c a a b c
            i k        
t:       a b c          
            j          

  然后接着比较,发现失配,然后接着移动:

s: a b d a e c c a a b c
              i k      
t:         a b c        
              j        

  然后很多个表格:

s: a b d a e c c a a b c
                    i k
t:               a b c  
                    j  

  接着,我们发现匹配了,哈哈哈哈哈:

s: a b d a e c c a a b c
                  i    
                  a b c
                  j    

  输出就行了……极度愉快极度简单极度好想的算法。


代码

   KMP算法:

#include <iostream>
#include <string>
using namespace std;
string s,t;
int Next[1000005];
int main(){
    int i=0,j=0,ls,lt;
    cin >> s >> t;
    ls=s.size();
    lt=t.size();
    j=0;
    for(i=1;i<lt;i++){
        while(j>0 && t[i]!=t[j])
            j=Next[j-1];
        if(t[i]==t[j])
            j++;
        Next[i]=j;
    }
    j=0;
    for(i=0;i<ls;i++){
        if(j==lt){
            cout << i-lt+1 << endl;
            j=Next[j-1];
        }
        while(j>0 && s[i]!=t[j])
            j=Next[j-1];
        if(s[i]==t[j])
            j++;
    }
    if(j==lt)
        cout << i-lt+1 << endl;
    for(i=0;i<lt-1;i++)
        cout << Next[i] << " ";
    cout << Next[lt-1] << endl;
    return 0;
}

  Sunday算法:

#include <iostream>
#include <string>
#include <map>
using namespace std;
string s,t;
int Next[1000005];
map <char,int> f; //因为仅包含大写字母,否则的话,可以适当开大点,但我觉得不会超过100
int main(){
    int i=0,j=0,k=0,ls,lt;
    char c;
    cin >> s >> t;
    ls=s.size();
    lt=t.size();
for (i=lt-1;i>=0;i--) //构造失败指针 if(f[t[i]]==0) f[t[i]]=i+1; k=lt; while(k<=ls){ i=k-1; j=lt-1; while(j>=0 && s[i]==t[j]){ i--; j--; } if(j==-1) cout << k-lt+1 << endl; k+=lt-f[s[k]]+1; }// 其实到这里就可以结束了 j=0; for(i=1;i<lt;i++){ while(j>0 && t[i]!=t[j]) j=Next[j-1]; if(t[i]==t[j]) j++; Next[i]=j; } /*j=0; for(i=0;i<ls;i++){ if(j==lt){ cout << i-lt+1 << endl; j=Next[j-1]; } while(j>0 && s[i]!=t[j]) j=Next[j-1]; if(s[i]==t[j]) j++; } if(j==lt) cout << i-lt+1 << endl;*/ for(i=0;i<lt-1;i++) cout << Next[i] << " "; cout << Next[lt-1] << endl; return 0; }

 

转载于:https://www.cnblogs.com/skl-hray/p/7686905.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30640291/article/details/96577196

智能推荐

多渠道归因分析(Attribution):python实现Shapley Value(四)_shapley值法python-程序员宅基地

文章浏览阅读8k次,点赞7次,收藏84次。本篇主要是python实现马尔科夫链归因,关联的文章:多渠道归因分析(Attribution):传统归因(一)多渠道归因分析:互联网的归因江湖(二)多渠道归因分析:python实现马尔可夫链归因(三)文章目录1 概念1.1 夏普里值(Shapley Value)1.2 SHAP值和马尔科夫链 归因的比较2 python实现2.1 传统的shapley value方式2.2 传统shapley 升级版2.2.1 Simplified Shapley Value Method2.2.2 Orde_shapley值法python

身份证、手机号加密存储的一些思路_身份证号码加密解决方案-程序员宅基地

文章浏览阅读3.7k次,点赞2次,收藏7次。这两年国家越来越重要个人敏感信息的存储、传输与交换。在获取敏感个人信息时,例如,手机号、身份证,都需要主体的主动授权。0x01:敏感信息泄露有哪些途径明文存储,比如直接把手机号、身份证存储到数据库。如果数据的用户和密码被一些不应该的人员看到,获取;就很容易造成泄漏明文传输,比如没有对敏感信息进行RSA或者AES加密,就在网络中进行传输集团子公司或者与第三方系统进行系统对接时,交换敏感数据。就是把我方系统的一些敏感信息,没经授权就发生给了第三方公司0x02:解决敏感信息泄漏的最佳途径明文存储对数_身份证号码加密解决方案

TensorFlow 2.0 深度学习实战 —— 浅谈卷积神经网络 CNN_tensorflow深度学习网络-程序员宅基地

文章浏览阅读856次,点赞25次,收藏25次。卷积神经网络 CNN(Convolutional Neural Networks,ConvNet)是一种特殊的深度学习神经网络,近年来在物体识别、图像重绘、视频分析等多个层面得到了广泛的应用。 _tensorflow深度学习网络

Linux 测速(使用SpeedTest)_linux speedtest-程序员宅基地

文章浏览阅读7.1k次。speedtest是一款使用python语言编写的轻量级的 linux 命令行测速工具,在python2以及python3的环境下都可以运行,基于 speedtest.net 基础框架来测量网络的上下行数据,安装也很简单,只要下载对应的 python 文件执行即可。_linux speedtest

Windows环境部署Flink 1.12版本问题解决_flink中为什么没有start-cluster.bat-程序员宅基地

文章浏览阅读2.2k次,点赞3次,收藏4次。从1.10.x还有就没有.bat了,可以从以前版本拷贝直接通过start-cluster.bat启动会拉起JobManager和TaskManager,如果下列参数不配置的话,TaskManager进程会退出,日志显示相关的参数没有配置,在conf/flink-conf.yaml中添加下面的配置,重新启动即可:默认启动是报错的,taskmanager.cpu.cores 未配置等,加上如下即可。taskmanager.cpu.cores: 2taskmanager.memory.task._flink中为什么没有start-cluster.bat

Div 与 table 的区别_hbuildr中div与表格的区别-程序员宅基地

文章浏览阅读946次。1:速度和加载方式方面的区别div 和 table 的差异不是速度,而是加载方式,速度只能是指网络速度,如果速度足够快,是没有差异的:div 的加载方式是即读即加载,遇到 没有遇到 的时候一样加载 div 中的内容,读多少加载多少;table 的加载方式是完成后加载,遇到 后,在读到 之前,table 中的内容不加载,或者传输中断了(document.onload()事件)_hbuildr中div与表格的区别

随便推点

freeswitch智能语音开发之ASR_freeswitch asr-程序员宅基地

文章浏览阅读5.1k次,点赞2次,收藏18次。ASR(Automatic Speech Recognition)自动语音识别技术是一种将人的语音转换为文本的技术。一、freeswitch如何使用asrfreeswitch提供两个app功能detect_speech和play_and_detect_speech给用户调用,detect_speech是异步的,play_and_detect_speech是同步的。1、detect_speech1.1语法:mod_name: 识别模块名称 如ali_asr[:params],其中params是param1=v_freeswitch asr

四元数的表示形式Hamilton & JPL定义_jpl quaternion-程序员宅基地

文章浏览阅读1k次。文章目录目录1.引言2.JPL定义1.引言Quaternion(四元数)是一种三维空间旋转的表示方法,四元数由一个实部和三个虚部构成,写如其中 i, j, k 为虚部的三个基:不是所有的四元数对于基的关系的定义都是一致的,下文描述两种定义形式:Hamilton & JPL,它们的区别及影响。2.Hamiltion定义ijk=−1四元数转换为旋转矩阵MatJPL左四元数 - 乘积矩阵(left-quaternion-product matri_jpl quaternion

Oracle-AWR报告生成方法_oracleawr报告生成-程序员宅基地

文章浏览阅读1.2k次。oracle-AWR报告生成方法_oracleawr报告生成

解决 undefined function bcdiv()_fatal error: uncaught error: call to undefined fun-程序员宅基地

文章浏览阅读5.2k次。Ubuntu16.04 php7.0 遇到问题:Uncaught Error: Call to undefined function bcdiv()执行下边的语句完美解决:sudo apt-get install php7.0-bcmath_fatal error: uncaught error: call to undefined function bcdiv() in /box/scri

MYSQL 数据库配置优化_skip_ssl-程序员宅基地

文章浏览阅读8.5k次,点赞2次,收藏28次。1、skip_ssl开启HTTPS后会出现内存不足,那是因为,在开启HTTPS访问时会在Apache中新开了一个开放了443端口的虛拟机。为了性能,通常我们会禁用SSL,添加如下参数skip_ssl;查看是否开启SSL: SHOW VARIABLES LIKE ‘%ssl%’; (have openssl表示是否支持SSL)修改配置文件my.cnf,加入以下内容:# disable_sslskip_ssl重启MySQL: service mysqld restart2、max_allo_skip_ssl

UE4-(反射)平面反射_ue平面反射-程序员宅基地

文章浏览阅读1.1w次,点赞10次,收藏44次。一、创建注意:平面反射拖拽到场景后,会创建一大块平面,这个平面是临时创建的,当程序运行后,我们不会看到这个平面。平面反射会捕获反射信息,只能用于平面反射效果,所以只适用于平整的对象,例如镜子,水池水面、窗户玻璃。任何尺寸较小表面平整的对象,都可以用平面反射覆盖。二、属性1.平面反射既可以实时渲染,也可以一次性捕获(属性面板中设置)平面反射使用实时渲染,性能开销非常大,因为平面反射实际上将从反射方向再次对关卡进行渲染。所以要比屏幕空间反射更加精确。对比:左侧屏幕空间反射.._ue平面反射

推荐文章

热门文章

相关标签