前言

现在有两个字符串:\(s1\)和\(s2\),现在要你输出\(s2\)在\(s1\)当中每一次出现的位置,你会怎么做?

暴力匹配算法

基本思路

用两个指针分别指向当前匹配到的位置,并对当前状态进行分类讨论:若相同则继续往下匹配,否则回溯

大致思路

用\(i\)来存储\(s1\)当前匹配到的位置,用\(j\)来存储\(s2\)当前匹配到的位置,则可得初始状态下\(i=j=0\)。

对于当前状态,有两种可能性:

①:\(s1[i]==s2[j]\)。则\(i++,j++\)

②:\(s1[i]!=s2[j]\)。则\(i-=(j-1),j=0\)

评价

时间复杂度:\(O(nm)\)。显然,这个方法效率并不高,每一次回溯要耗去大量时间,能不能进行优化呢?

\(KMP\)算法

简介

\(KMP\)算法是对暴力匹配算法的改进,由\(D.E.Knuth\),\(J.H.Morris\)和\(V.R.Pratt\)同时发现,因此人们称它为\(Knuth-Morris-Pratt\)算法(简称\(KMP\)算法)。

基本思路

\(KMP\)算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个\(Next\)函数,函数本身包含了模式串的局部匹配信息。

大致思路

还是用\(i\)来存储\(s1\)当前匹配到的位置,用j来存储\(s2\)当前匹配到的位置,则可得初始状态下\(i=j=0\)

对于当前状态,有两种可能性:

①:\(s1[i]==s2[j]\)。则\(i++,j++\)

②:\(s1[i]!=s2[j]\)。则\(j=Next[j]\)(i不变)

其中\(Next\)数组存储的是当前这一位的部分匹配值(这在后面会详细介绍),所以只要让\(j\)变成\(Next[j]\),就可以继续对当前字符串进行匹配了,省去了i回溯所耗去的大量时间

\(Next\)数组

在匹配过程中,你可以发现一个基本事实是:当\(s1[i]\)与\(s2[j]\)不匹配时,你其实知道前面\(j-1\)字符是什么。

\(KMP\)算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。

所以,我们就可以把当前所得到的部分匹配值给求出来。又由于对于同一个字符串,部分匹配值是固定不变的,所以可以把它存在\(Next\)数组里。

那么\(Next\)数组怎么求呢?

记得某大佬说过这样一句话:

\(Excerpt\)

求\(Next\)数组的过程就是一个\(KMP\)的过程。

首先,令\(i=0\),\(j=-1\),\(Next[0]=-1\),且当前要求的是\(Next[i+1]\)。则对于当前状态,有两种可能性:

①\(j==-1\)或\(s2[i]==s2[j]\)。则\(i++,j++,Next[i]=j\)

②\(j!=-1\)且\(s2[i]!=s2[j]\)。则\(j=Next[j]\)//把\(j\)赋值为\(j\)的部分匹配值

这样就可以轻松求出\(Next\)数组了。

代码

#include<bits/stdc++.h>
#define N 1000000
#define pc(ch) (pp_<100000?pp[pp_++]=ch:(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=ch))
int pp_=0;char pp[100000];
using namespace std;
int len1,len2,Next[N+5];//len1存储s1的长度,len2存储s2的长度,这样不用调用strlen(),strlen()会超时;Next[]存储部分匹配值
char s1[N+5],s2[N+5];
inline void write(int x)
{
if(x>9) write(x/10);
pc(x%10+'0');
}
inline void GetNext()//求出Next[]数组
{
register int i=0,j=Next[0]=-1;//初始化
while(i<=len2)//类似于一个KMP的过程
{
if(j==-1||s2[i]==s2[j]) i++,j++,Next[i]=j;
else j=Next[j];
}
}
int main()
{
register int i=0,j=0;
scanf("%s%s",s1,s2),len1=strlen(s1),len2=strlen(s2),GetNext();
while(i<=len1)//KMP的过程
{
if(j==-1||s1[i]==s2[j]) {++i;if(++j==len2) write(i-len2+1),pc('\n'),j=Next[j];/*如果找到答案就输出*/}
else j=Next[j];//如果匹配失败,就更新j为其部分匹配值
}
for(i=1;i<=len2;++i) write(Next[i]),pc(' ');//依照题意输出Next[]数组
return fwrite(pp,1,pp_,stdout),0;
}

最新文章

  1. HTML页面如何判断是手机访问还是电脑访问
  2. Python中的绝对路劲和相对路径
  3. 【转载】 Android PullToRefresh (ListView GridView 下拉刷新) 使用详解
  4. 条件随机场matlab程序下载
  5. 【Docker】docker /var/lib/docker/aufs/mnt 目录满了,全是垃圾数据,咋搞?
  6. HttpClient_001_初步实现项目01的servlet,与项目02的servlet,之间数据访问
  7. How to browse the entire documentation using XCode 5 Documentation and API Reference ?
  8. Ztack学习笔记(1)-初识Ztack
  9. Unique Paths II ——LeetCode
  10. 微信电脑版(Mac和Windows)安装
  11. win7+ ubuntu 双系统
  12. Winform界面中实现菜单列表的动态个性化配置管理
  13. ZOJ Problem Set - 3593 拓展欧几里得 数学
  14. linux文本处理三剑客的学习
  15. 在Linux下用gcc编译hello world
  16. Laravel: 基础篇
  17. CentOS7安装RabbitMQ
  18. JXL读取,写入Excel
  19. [Shiro] - Shiro之SpringBoot中的使用
  20. Freemarker不显示对象的属性

热门文章

  1. gulp使用文档
  2. angular1 表单验证demo
  3. 2017-10-6 清北刷题冲刺班a.m
  4. 为CentOS下的Docker安装配置python3【转】
  5. POJ1009 Edge Detection
  6. Leetcode初级算法(排序和搜索+数学篇)
  7. sweetAlert()参数配置
  8. Storm概念学习系列之事务
  9. c语言字符串操作总结(转)
  10. 借鉴redux,实现一个react状态管理方案