题目传送门

解题思路:

首先说KMP的作用:对于两个字符串A,B(A.size() > B.size()),求B是否是A的一个字串或B在A里的位置或A里有几个B,说白了就是字符串匹配.

下面创设一个问题背景 : 有两个字符串A,B,求B在A中的位置.

A : abcdcbd

B: cdcb

对于上述问题,我们最先想到也就是最暴力的办法,就是把B整体从1~7每次一格的往右挪,每往挪一格,便把B从头到尾跟A匹配一遍,直到匹配不成功或全部匹配成功,如果匹配不成功,就再整体往右挪一格,这样的时间复杂度最坏可以达到O(nm).

如果字符串再长一点,长度达到1e6,显然暴力是会TLE的,那我们就要考虑优化,上述做法唯一能优化的地方就是往右挪的方式,因为一格一格的挪实在是太笨了,所以我们就要想怎样才能挪的快而且正确呢?

很容易想到的是1.直接移到B的后一位 2.移到失配的位置 ,而这两种做法都是可以被推翻的,见图:

那我们就只能甘于暴力吗?不,所以我们要学习KMP,进入正题:

对于学习KMP,要明白几个重要概念:

对于一个字符串A': abcddc

1.前缀:a,ab,abc,abcd,abcdd.

2.后缀:c,dc,ddc,cddc,bcddc.(前缀和后缀都不包括其本身)

然后我们来看一下KMP的伪代码:

 while(i < a.length() && j < b.length()) {
if(当前位置匹配) i++,j++;
else j = kmp[j]; //跳到一个特定的位置
}
if(j == b.length()) 成功
else 失败

看完后发现其实KMP的核心,就是求kmp[j](大多数人称为next[j]),下面就来说一下kmp[j]怎么求.

kmp[i]表示的是长度为i(1~i)的字符串的最长公共前后缀的长度.(以下的j皆为不包括本次字符求得的最长公共前后缀长度,其实也就是枚举前缀的长度)

例: P: a b c d a b c

kmp[]:0 0 0 0 1 2 3

求当前kmp[i],可以分两种情况:

1.当P[i] == P[j+1]时(见下图),kmp[i] = j+1,因为j=2说明P[1..2]和P[3..4]是完全相等的,而P[5]==P[3],那么P[1..3]和P[3..5]是完全相等的,那kmp[5] = 3;

2.当P[i] != P[j+1]时(见下图),j = kmp[j]往回跳,因为j == 3,说明P[1..3]和P[5..7]完全相等,而后缀的最后加上一个P[i],则意味着我们相当于在P[4]插入一个P[i],要在P[1..3]找一个位置j,使P[i] == P[j+1],转化为第一种情况,求kmp[4],即为kmp[i].那为什么j=kmp[j]呢,举个例子:aba插入一个b,只有两个a相等,后面才有可能成为公共前后缀.还有就是j要跳到什么程度呢?答案就是,当找到P[i]==P[j+1]时,或j跳到0了,说明不可能形成公共前后缀了,就停止.

下面上求kmp[]的代码:

 string l;
j = ;
kmp[] = ;
for(int i = ;i <= n; i++) {
while(j != && l[i] != l[j+]) j = kmp[j];
if(l[i] == l[j+]) j++;
kmp[i] = j;
}

然后,我们再回归问题,如何在A串中匹配B呢?Talk is cheap,show you the code.

 string a,b;
j = ;
kmp[] = ;
for(int i = ;i <= n; i++) {//n为B的长度
while(j != && b[i] != b[j+]) j = kmp[j];
if(b[i] == b[j+]) j++;
kmp[i] = j;
}
j = ;
for(int i = ;i <= m; i++) {//m为A的长度
while(j != && a[i] != b[j+]) j = kmp[j];//匹配失败就往回跳
if(a[i] == b[j+]) j++;//当前匹配成功
if(j == n) {//整个B匹配成功
printf("%d",i - j + );
return ;
}
}

最后说一点,就是对于A和B匹配的时候,并不是i在向后动,而是j在向前动.

最后的吐槽时间:这算法好难理解(自认为),想出这算法的三个人也太nb了,写这篇博客好累,虽然不一定有人看,但心里还是挺高兴的.

AC代码:

//洛谷题的AC代码

 #include<iostream>
#include<cstdio> using namespace std; string l1,l,pp,p2;
int lena,lenb,next[],j; int main() {
l1 = l = " ";
cin >> pp >> p2;
l += pp;l1 += p2;
lena = l.length();
lenb = l1.length();
next[] = ;
for(int i = ;i < lenb; i++) {
while(j != && l1[i] != l1[j+]) j = next[j];
if(l1[j+] == l1[i]) j++;
next[i] = j;
}
j = ;
for(int i = ;i < lena; i++) {
while(j != && l[i] != l1[j+]) j = next[j];
if(l[i] == l1[j+]) j++;
if(j == lenb - ) {
printf("%d\n",i - lenb + );
j = next[j];
}
}
for(int i = ;i < lenb; i++)
printf("%d ",next[i]);
return ;
}

KMP【模板】 && 洛谷 P3375的更多相关文章

  1. KMP字符串匹配 模板 洛谷 P3375

    KMP字符串匹配 模板 洛谷 P3375 题意 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.(如果 ...

  2. 洛谷 P3375 【模板】KMP字符串匹配 || HDU 1686 Oulipo || kmp

    HDU-1686 P3375 kmp介绍: http://www.matrix67.com/blog/archives/115 http://www.cnblogs.com/SYCstudio/p/7 ...

  3. 【数论】卢卡斯定理模板 洛谷P3807

    [数论]卢卡斯定理模板 洛谷P3807 >>>>题目 [题目] https://www.luogu.org/problemnew/show/P3807 [输入格式] 第一行一个 ...

  4. 洛谷 P3375 【模板】KMP字符串匹配

    我这段时间因为字符串太差而被关了起来了(昨晚打cf不会处理字符串现场找大佬模板瞎搞,差点就凉了),所以决定好好补一下字符串的知识QAQ,暂时先学习kmp算法吧~ 题目链接:https://www.lu ...

  5. 洛谷P3375 [模板]KMP字符串匹配

    To 洛谷.3375 KMP字符串匹配 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果 ...

  6. 洛谷 P3375 【模板】KMP字符串匹配 题解

    KMP模板,就不解释了 #include<iostream> #include<cstdio> #include<cstring> #include<algo ...

  7. 【模板】LIS模板 洛谷P1091 [NOIP2004提高组]合唱队形 [2017年4月计划 动态规划11]

    以题写模板. 写了两个:n^2版本与nlogn版本 P1091 合唱队形 题目描述 N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形. 合唱队形是指这样的一种队 ...

  8. 树链剖分模板(洛谷P3384)

    洛谷P3384 #include <bits/stdc++.h> #define DBG(x) cerr << #x << " = " < ...

  9. 洛谷—— P3375 【模板】KMP字符串匹配

    P3375 [模板]KMP字符串匹配 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next. (如 ...

随机推荐

  1. php 低版本不能使用php 命令,创建软链接

      ln -s /usr/local/php5/bin/php /usr/bin/php php 低版本不能使用php 命令,创建软链接   phpize 依赖于 phpcli 模式 所以php命令必 ...

  2. PageObject

    import org.openqa.selenium.WebDriver; import org.openqa.selenium.ie.InternetExplorerDriver; import o ...

  3. PAT A1151 LCA in Binary Tree

    利用树的前序和中序递归判定最小公共祖先~ 直接根据两个序列递归处理~ #include<bits/stdc++.h> using namespace std; ; int N,M; int ...

  4. java_3:JVM、JRE、JDK区别和联系

    首先 三者之间存在包含关系JVM + 核心类库 = JREJRE + java开发工具(javac.exe/jar.exe) = JDK 什么是JVM? 我们知道Java语言有一个独特的优点就是可以跨 ...

  5. LinkedHashMap的用法

    1:LinkedHashMap的简介 Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序.此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表.此链接列表定义了迭 ...

  6. CF1285D Dr. Evil Underscores

    挂个链接 Description: 给你 \(n\) 个数 \(a_1,a_2,--,a_n\) ,让你找出一个 \(x\) ,使 \(x\) 分别异或每一个数后得到的 \(n\) 个结果的最大值最小 ...

  7. Python 基础之面向对象之常用魔术方法

    一.__init__魔术属性 触发时机:实例化对象,初始化的时候触发功能:为对象添加成员,用来做初始化的参数:参数不固定,至少一个self参数返回值:无 1.基本用法 #例:class MyClass ...

  8. 那些年我们踩过的坑,SQL 中的空值陷阱!

    文章目录 NULL 即是空 三值逻辑 空值比较 NOT IN 与空值 函数与空值 DISTINCT.GROUP BY.UNION 与空值 ORDER BY 与空值 空值处理函数 字段约束与空值 SQL ...

  9. 【AOP】在Aspect中无法获取实现类方法的注解

    问题 MethodSignature methodSignature = (MethodSignature) (joinPoint.getSignature()); Method method = m ...

  10. QQ企业通知识点---ClassSerializers

    Serializers  串列器 序列化器 串行器 MemoryStream   创建其支持存储区为内存的流. BinaryFormatter   以二进制格式将对象或整个连接对象图形序列化和反序列化 ...