关于KMP
KMP算法,对于求b串在a串中出现的次数。
在学习KMP之前,希望大家充分掌握hash。
HASH:
1.hash表:用来离散化(hash数组,hash链表)
2.Rabin-Kap算法:
可替代KMP(O(n)),Manacher(O(n))等;
hs[t]=hs[t-1]*p+s[t];
hash(x,y)=hs[y]-hs[x-1]*p^(y-x+1);
哈希是字符串题目的基础(个人觉得)
一般情况下,hash是可以替代KMP的。
但我们为什么还要学KMP呢?
众所周知,hash会产生hash冲突。于是kmp就上场了。
我们由一道题来引入正题:
很明显,次数为3(abababa)
有三种方法:
一、暴力
枚举左端或右端点,另一个端点根据S2确定,线性扫一遍当前区间,检查是否匹配。
If(匹配) ans++;
时间复杂度: O(n^2) 在此不再赘述。
二、哈希
在暴力的基础上,扫描区间检查的时候是O(1)的。
总时间复杂度: O(n)
三、 KMP算法
而KMP算法也可以在O(n)的时间内求出答案。
暴力匹配:
每次从A字符串的第i位,B字符串的第1位开始逐一比较,相等则继续下一位比较,如果能一直比较到B字符串的末尾,则找到了一次匹配
最坏情况:
A=aaaaaaaaaaaaaaaaaaaa
B=aaaaaaaab
设A的长度为N,B的长度为M
时间复杂度为O(MN)
KMP算法
f[i]:最大的k,使得A(1..i)的子串的后k位等于B的前k位
i = 7
A = abababaababacb
B = ababacb
f[i] = 5
B在A中出现的次数 等于 满足f[i]=(B的长度)的i的个数
转化为快速求f[i]数组
关于转化:
关于代码实现:
辅助数组next:
代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e6+;
char s1[N],s2[N];
int cas,l1,l2,fail[N];
void get_next(){
int p=;fail[]=;
for(int i=;i<=l2;i++){
while(p>&&s2[i]!=s2[p+]) p=fail[p];
if(s2[i]==s2[p+]) p++;
fail[i]=p;
}
}
void kmp(){
int p=,ans=;
for(int i=;i<=l1;i++){
while(p>&&s1[i]!=s2[p+]) p=fail[p];
if(s1[i]==s2[p+]) p++;
if(p==l2) ans++,p=fail[p];
}
printf("%d\n",ans);
}
int main(){
for(scanf("%d",&cas);cas--;){
scanf("%s%s",s2+,s1+);
l1=strlen(s1+);l2=strlen(s2+);
get_next();
kmp();
}
return ;
}
关于KMP的应用:
应用1.
给定一个字符串A,求最短的字符串B,使得A是若干个B连接成的字符串的前缀 。
若A=abcabcab
则B=abc
解法:
求出A串在KMP算法中A的next数组
设A的长度为N
则答案为A的前N-next[N]位
显然[nxet[N]+1,N]是循环节
为什么呢?
我们可以分两种情况讨论:
1.next[N]>N/2
2.nextN]<=N/2
应用2.
给定一个字符串A,求最短的字符串B,使得A是若干个B连接成的字符串的子串
若A=bcabcabc
则B=abc
解法:
其实和上一题一毛一样
A=bcabcabc
若B是一个循环节,则把B循环节移位一下仍是循环节。
abc->bca
应用3:
给出字符串A和B,求在A中最多能选出多少个互不重叠的B串
A=abababab
B=aba
最多选出两个:abababab
解法:
每次贪心地选出最靠左的一个B串即可
KMP匹配时记下上次完全匹配的位置,遇到完全匹配时判断是否和上次的位置重叠即可
应用4:
给定一个字符串A
对于A的每个前缀A(1...i),求最长的字符串Bi,使得len(Bi)<i,且A是若干个Bi连接成的字符串的前缀
求每个Bi,len(A)<=10^6
解法:
将每个Bi称作循环节,最长的循环节不一定是最短循环节重复若干遍
aabbaa
最短: aabb
最长: aabba
求next数组
对于每个前缀A(1…i), A(1…i-next[i]), A(1…inext[next[i]])……都是它的循环节
沿着next指针往前跳,直到跳到0之前
对每个i直接跳: O(N2)
递推:记min[i]表示从i开始沿next往前跳最小能跳到多少
时间复杂度:O(N)
应用5:
在N*M字符矩阵中找出一个最小子矩阵,使其多次复制所得的矩阵包含原矩阵。 N<=10000,M<=75
解法:
先找出最大的K,使得原矩阵是若干个K*M的矩阵拼成一列后的子矩阵
把一行看做一个整体,对列做KMP
用应用1的方法确定最小行宽
再在K*M的矩阵中,把一列看做一个整体,用同样的方法求最小行宽
时间复杂度:O(N*M)
应用6:
字符集中有一些字符(最多26个), 给出每个字符的出现概率(它们的和保证为1)
再给出一个子串B,长为M
求:任给一个长度为N的字符串A(只能包含字符集中的字符),使得S是A的子串的概率。
N<=100
解法:
动态规划
想象一边随机生成字符串A,一边用KMP匹配字符串B的过程
f[i][j]表示随机生成到第i位,此时B串匹配到第j位的概率
枚举下一位生成字符c,设其生成概率为gc
假设下一位填c,计算出KMP匹配指针j应该移动到j‘
f[i+1][j’] += f[i][j]*gc
已经匹配到第m位的状态不再进行转移
ans = ∑f[i][m]
应用7:
给定一个数字串A,不含前导0,长为m。 m<=9
求第P小的包含子串A的数字
P<=109
解法:
答案最多18位
二分答案X,转为判断小于等于X的包含子串A的数字有多少个
F[i][j][k][l]表示,填完前i位, KMP指针指向A的第j位,之前是否出现过子串A的状态为k(0/1),下一位能否任意填数的状态为l(0/1),的方案数
答案为∑f[18][j][1][l]
应用8:
有一枚硬币,抛到正面的概率是a/b,反面概率是1-a/b
不停地抛硬币,将得到的结果用01序列记录下来, 0表示反面, 1表示正面
给定01序列A,长为n,求期望抛几次可以在结果序列中找到子串A
n<=1000, 0<=a,b<=100
答案用最简分数形式输出
解法:
想象一边随机生成序列一边在A串上移动KMP匹配指针的过程
f[i]表示当指针在A的第i位时, 期望抛几次可以抛出A串
设: i0为下一位抛出0时指针对应的位置, i1为下一位抛出1时指针对应的位置
f[n] = 0
f[i] = 1+p*f[i1]+(1-p)*f[i0] (i<n)
解方程组
太麻烦(而且要求答案保留分数形式)
注意到, i0、 i1中必有一个等于i+1,另一个小于I
当i0=i+1时:f[i] = 1+(a/b)*f[i1]+(1-a/b)*f[i0]
当i1=i+1时:
f[i+1] = (b/a)*(f[i]-1-(1-a/b)*f[i0])
递推即可
一世安宁
关于KMP的更多相关文章
- KMP算法求解
// KMP.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using namespac ...
- 简单有效的kmp算法
以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...
- KMP算法
KMP算法是字符串模式匹配当中最经典的算法,原来大二学数据结构的有讲,但是当时只是记住了原理,但不知道代码实现,今天终于是完成了KMP的代码实现.原理KMP的原理其实很简单,给定一个字符串和一个模式串 ...
- 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)
前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...
- [KMP]【学习笔记】
Oulipo Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 36916 Accepted: 14904 Descript ...
- KMP算法实现
链接:http://blog.csdn.net/joylnwang/article/details/6778316 KMP算法是一种很经典的字符串匹配算法,链接中的讲解已经是很明确得了,自己按照其讲解 ...
- KMP专题
1.[HDU 3336]Count the string(KMP+dp) 题意:求给定字符串含前缀的数量,如输入字符串abab,前缀是a.ab.aba.abab,在原字符串中出现的次数分别是2.2.1 ...
- KMP学习之旅
说起kmp就要从字符串的匹配说起,下面我们谈谈字符串的匹配 给定一个原字符串:bababababababababb,再给定一个模式串:bababb,求模式串是否在源字符串中出现 最简单的方法就是遍历源 ...
- KMP模板
参考:http://www.cnblogs.com/c-cloud/p/3224788.html #include<stdio.h> #include<string.h> vo ...
- 【字符串匹配】KMP算法和next数组的c/c++实现
KMP算法基本思想有许多博客都写到了,写得也十分形象,不懂得可以参考下面的传送门,我就不解释基本思想了.本文主要给出KMP算法及next数组的计算方法(主要是很多网上的代码本人(相信应该是许多人吧)看 ...
随机推荐
- python基础一数据类型之字符串
摘要: python基础一中有字符串,所以这篇主要讲字符串. 一,字符串的注释 二,字符串的索引与切片 三,字符串的方法 一,字符串的注释 单引号 双引号 三引号都可以用户定义字符串.三引号不仅可以定 ...
- oracle EBS rtf报表不能输出模板样式
1.需要定义中文的数据定义 2.缺少文件 cd $ADMIN_SCRIPTS_HOME prefs.ora 3.查看文档 文档 ID 1059712.1 (1)请求模版显示不出来 解决:模版定义中模 ...
- 转:ASP.NET MVC 多语言实现技巧 最简、最易维护和最快速开发
说说传统做法的缺点 1.做过多语言的都知道这玩意儿太花时间 2.多语言架构一般使用资源文件.XML或者存储数据库来实现.这样就在一定程序上降低了性能 3.页面的可读性变差,需要和资源文件进行来回切换 ...
- 根据IP查找计算机名
查找10.10.10.X段的计算机名称 $r="10.10.10." $s=1..254 foreach ($a in $s) { $ip = $r + [string]$a $i ...
- Python实例--12306的抢票功能
基础知识学习 目标: 通过python程序实现自动登录下单功能 知识点: Selenium + 云打码 + Python 学习链接: 1. Python学习--Selenium模块 2. Python ...
- Hadoop HBase概念学习系列之HBase里的HRegion(五)
首先,要区分,HRegion服务器包含两大部分:HLog部分和HRegion部分 HBase里的HRegion服务器 HBase里的HRegion 当表的大小超过设置值的时候,HBase会自动将表划 ...
- Django商城项目笔记No.9用户部分-注册接口签发JWTtoken
Django商城项目笔记No.9用户部分-注册接口签发JWTtoken 我们在验证完用户的身份后(检验用户名和密码),需要向用户签发JWT,在需要用到用户身份信息的时候,还需核验用户的JWT. 关于签 ...
- 【Python】【unittest】unittest测试框架中setup,teardown与setupclass,teardownclass的区别
# -*- coding:utf-8 -*- import unittest def runTest(testcaseclass,testcase=[]): suite = unittest.Test ...
- Flex 布局知识点梳理
传统的布局方案,在针对特殊布局时会很不方便,比如垂直居中,把一个容器等分为N列等等.自从 Flex 出现以后,这些都迎刃而解了,本文对Flex相关内容做一个简单梳理. 什么是 Flex Flex 是 ...
- Python在Win10系统的安装和使用配置
Python是一种计算机程序设计语言.你可能已经听说过很多种流行的编程语言,比如非常难学的C语言,非常流行的Java语言,适合初学者的Basic语言,适合网页编程的JavaScript语言等等. 下载 ...