串和KMP算法
一、串
串是由零个或多个字符串组成的有限序列
(一)、串的定义
定长顺序存储
特点:每个串变量分配一个固定长度的存储区,即定长数组
定义:
#define MAXLEN 255
typedef struct{
char ch[MAXLEN];
int length;
}SString;
堆分配存储表示
这里的堆是指c语言中存在一个称之为"堆"的自由存储区,这里的堆是一个链表的结构,和数据结构中的堆是不同的!
特定:存储空间在程序执行过程中动态分配
定义
typedef struct{
char *ch;
int length;
}HString;
块链存储表示
- 特点:使用链表结构,每个节点可以存储4个字符
(二)、最小操作集
- 串赋值
- 串比较
- 求串长
- 串联结
- 求子串
二、串的模式匹配
模式匹配:子串的定位操作
(一)、简单的模式匹配算法
定义:暴力匹配算法
功能:在主串
s1中查找子串s2,如果找得到就返回位置(下标+1),否则返回-1思路:以在主串
abababc中匹配子串abc为例设
i为当前匹配主串的位置,j为匹配子串的位置- 匹配
s1[i]是否等于s2[j],相等到第二步,不相等则到第三步 - 相等,
i++,j++ - 不相等,使
i=i-j+1,j==0,倒退重新匹配 - 重复以上操作,直到
i==strlen(s1)或j==strlen(s2)
- 匹配
code
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#include <cstring> using namespace std; int cmp(string s1,string s2){ //s1主串,s1子串
int ans,i=0,j=0,len1=s1.length(),len2=s2.length();
while(i<len1&&j<len2){ //注意len与下标差1
//cout << "i:" << i << " j:" << j << endl;
if(s1[i]==s2[j]){
i++,j++;
}else{
i=i-j+1; //上一个匹配初始位的下一个pos
j=0;
}
}
ans=j==len2?i-j:-1;
return ans+1; //注意pos是下标位置,下标->位置
} int main(){
string s1,s2;
cin >> s1 >> s2;
int pos = cmp(s1,s2);
printf("pos:%d\n",pos);
}
时间复杂度:
O(m*n)(设strlen(s1)=n,strlen(s2)=m)主要存在的问题:
- 显然s1至少要匹配
n-m+1次 - 我们可以想办法通过空间换时间的方式减少s2的匹配次数(即想办法膜除第三步中回退的过程)
- 显然s1至少要匹配
(二)、KMP算法
解决暴力匹配过程中回退的问题
相关概念
- 前缀:除最后一个字符,字符串的所有头部子串
- 后缀:除第一个字符外,字符串的所有尾部子串
- 部分匹配:字符串的前缀和后缀的最长相等前后缀长度
具体操作
列出最长相等前后缀长度
以待匹配字符串
ababa为例序号 子字符串 前缀 后缀 最长相等前后缀长度 1 a 0 2 ab b a 0 3 aba a,ab ba,a 1 4 abab a,ab,aba bab,ab,b 2 5 ababa a,ab,aba,abab baba,aba,ba,a 3 构建部分匹配值表(Partial Match)
编号 1 2 3 4 5 s a b a b a pm(上表最后一列) 0 0 1 2 3 使用(为了方便理解算法,我们这里用1作为下标起点)
操作:
匹配
s1[i]是否等于s2[j]相等,
i++,j++不相等,
j=j-(j-1-pm[j-1]),即使子串回退回退的距离move=已匹配的字符数-对应的部分匹配值=
j-1-pm[j-1]重复以上操作,直到
i>=strlen(s1)或j>=strlen(s2)
如:
s1:
abacdababas2:
ababa- 当i=4,j=4,显然
s1[i]!=s2[j] j=4-(4-1-pm[4-1])=2,i不需要回退
优化pm表
存在问题:
pm[5]对应第6个字符匹配失败,显然是用不到的
优化:将pm表整体右移一格构成一张新表称为next,表示子串下一个应该匹配的位置,使
next[1]=-1编号 1 2 3 4 5 s a b a b a next -1 0 0 1 2 此时:
move=j-1-next[j]j=j-move=j-(j-1-next[j])=next[j]+1注:关于这里使用-1,王道给出的解释是"因为若是第一个元素匹配失败,则需要将子串向右移动一位,而不需要计算子串移动的位数",简单来说就是此时只要将主串左移,不需要move.(我靠,NewBee,写到这里突然悟了!!小黄鸭原理可以的.)
继续改进:显然,我们可以之际在next[j]上加1出
编号 1 2 3 4 5 s a b a b a next 0 1 1 2 3 注:(这里再备注一下next下标的意义)
next[i]=0,表示没有一个前缀可以匹配,主要作为标识符使用next[i]=j],j表示有i个前缀可以匹配
此时:
move=next[j]j=next[j]
推理next数组的一般公式
next函数的一般公式(设首位下标为1):
next[j]=0,j=1(即第一位不匹配时需要移动)next[j]=max{k|1<k<j且\(`P_1...P_{k-1}`=`P_{j-k+1}...P_{j-1}`\)}- 1,其它
尝试通过已知
next[j]推导next[j+1]:令
k=next[j],s2为子串k=0,则next[j+1]=next[j]+1s2[j]=s2[k],则next[j+1]=next[j]+1s2[j]!=s2[k],则k=next[k],继续循环匹配
编写代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
string s1,s2;
int nextValue[N];
void getNext(string s,int *nextValue){
int now=1,k=0,len=s.length();
nextValue[0]=-1; //设定nextValue[0]=-1,作为一个特殊的标识符表示第一个值没有匹配到
while(now<len){
if(k==-1){ //递归出口
nextValue[++now]=++k;
continue;
}
if(s[now]==s[k]){ //递归体
nextValue[++now]=++k;
}else{
k=nextValue[k];
}
}
}
//找得到返回第一次出现的pos,否则返回-1
int kmpCmp(string s1,string s2){
int i=0,j=0,ans=-1;
int len1=s1.length(),len2=s2.length();
while(i<len1&&j<len2){
if(j==-1||s1[i]==s2[j]){
i++;
j++;
}else{
j=nextValue[j];
}
}
if(j==len2){
ans=i-len2+1;
}
return ans;
}
int main()
{
cin >> s1;
cin >> s2;
getNext(s2,nextValue);
cout << kmpCmp(s1,s2);
return 0;
}
注:感觉在局部有点问题,没有找到好的测试样例
优化
问题产生:显然当
s1[i]!=s2[j]时,我们会置换j=next[j].而当s1[i]!=s2[j],j=next[j]时,显然s1[i]!=s2[next[j]],这是一次失效的匹配解决:当我们在创建
next数组时,我们可以先判断,再通过next[j]=[next[j]]来消除这一情况(记住,这一过程是近似递归的!)代码
#include <bits/stdc++.h> using namespace std; const int N=1e5+10; string s1,s2;
int nextValue[N]; void getNext(string s,int *nextValue){
int now=1,k=0,len=s.length();
nextValue[0]=-1; //设定nextValue[0]=-1,作为一个特殊的标识符表示第一个值没有匹配到
while(now<len){
if(k==-1){ //递归出口
nextValue[++now]=++k;
continue;
}
if(s[now]==s[k]){ //递归体
nextValue[++now]=++k;
}else{
k=nextValue[k];
}
}
} //找得到返回第一次出现的pos,否则返回-1
int kmpCmp(string s1,string s2){
int i=0,j=0,ans=-1;
int len1=s1.length(),len2=s2.length();
while(j<len2){
if(j==-1||s1[i]==s2[j]){
i++;
j++;
}else{
if(s1[j]!=s1[nextValue[j]]){
j=nextValue[j];
}else{
j=nextValue[nextValue[j]];
}
}
}
if(j==len2){
ans=i-len2+1;
}
return ans;
} int main()
{
cin >> s1;
cin >> s2;
getNext(s2,nextValue);
cout << kmpCmp(s1,s2);
return 0;
}
串和KMP算法的更多相关文章
- hdu 3336:Count the string(数据结构,串,KMP算法)
Count the string Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- 数据结构与算法JavaScript (五) 串(经典KMP算法)
KMP算法和BM算法 KMP是前缀匹配和BM后缀匹配的经典算法,看得出来前缀匹配和后缀匹配的区别就仅仅在于比较的顺序不同 前缀匹配是指:模式串和母串的比较从左到右,模式串的移动也是从 左到右 后缀匹配 ...
- 第4章学习小结_串(BF&KMP算法)、数组(三元组)
这一章学习之后,我想对串这个部分写一下我的总结体会. 串也有顺序和链式两种存储结构,但大多采用顺序存储结构比较方便.字符串定义可以用字符数组比如:char c[10];也可以用C++中定义一个字符串s ...
- 第十一章 串 (c3)KMP算法:理解next[]表
- 第十一章 串 (c2)KMP算法:查询表
- 第十一章 串 (c1)KMP算法:从记忆力到预知力
- 数据结构与算法5—KMP算法
串的模式匹配算法 子串(模式串)的定位操作通常称为串的模式匹配. 这是串的一种重要操作,很多 软件,若有“编辑”菜单项的话, 则其中必有“查找”子菜单项. 串的顺序存储实现 #include<s ...
- 《数据结构》之串的模式匹配算法——KMP算法
//串的模式匹配算法 //KMP算法,时间复杂度为O(n+m) #include <iostream> #include <string> #include <cstri ...
- 数据结构- 串的模式匹配算法:BF和 KMP算法
数据结构- 串的模式匹配算法:BF和 KMP算法 Brute-Force算法的思想 1.BF(Brute-Force)算法 Brute-Force算法的基本思想是: 1) 从目标串s 的第一个字 ...
随机推荐
- JVM调优——JVM监控工具jvisualvm的使用及GC插件安装
一.前言 在高并发的场景下,我们网站的的访问性能会降低,我们怎么优化,这是个问题!天天听JVM调优,实际上还是不知道怎么调优,调优也是看着网上说的修改一下JVM的堆的空间等等进行的.实际上我们应该在压 ...
- JVM学习——内存空间(学习过程)
JVM--内存空间 关于内存的内容,内存的划分.JVM1.7 -> 1.8的变化比较大 JVM指令执行的时候,是基于栈的操作.每一个方法执行的时候,都会有一个属于自己的栈帧的数据结构.栈的深度, ...
- 添加删除系统右键菜单(就是上下文菜单,也就是Context Menu)中的一些选项
随着电脑安装的东西越来越多,右侧菜单也原来越长,很不方面.所以打算清理一下 我删除的大约以下几个,友好一点的都可以配置.当然也可以通过注册表直接删除. 特:注册表备份,即导入导出,避免一失足成千古恨. ...
- curl的HTTP参数速查表
curl简介 curl是一个开源的命令行工具,它基于网络协议,对指定URL进行网络传输,得到数据后不任何具体处理(如:html的渲染等),直接显示在"标准输出"(stdout)上. ...
- 轩辕展览-为什么要做VR虚拟展厅设计?
沉浸感,有趣和互动体验VR虚拟展厅设计给客户带来高度的沉浸感和互动体验,给客户一种真实的感觉,让客户更愿意参与,使商家的宣传更加客观. 展示方式多样化 ,增加宣传优势在展示产品或企业时,VR全景可达到 ...
- SaaS平台是什么,为什么字节、腾讯等大厂都在抢相关人才
SaaS平台很多人可能没听说是什么,但是从事TO B公司的员工来说,SaaS平台应该都有所耳闻.从2016年开始,腾讯开始发力TO B算起,到处在挖TO B公司的骨干人才,而熟悉SaaS平台的人才竞 ...
- 解除Ubuntu禁止root远程登录
编辑SSH服务配置文件 编辑SSH服务的配置文件sshd_config,修改SSH的端口和root用户权限. 使用到的命令:(按字母 i 进入编辑模式,按ESC退出编辑模式, :wq 保存退出). r ...
- KTL 一个支持C++14编辑公式的K线技术工具平台 - 第四版,稳定支持Qt5编程,zqt5语法升级,MA函数提升性能1000%,更多公式算法的内置优化实现。
K,K线,Candle蜡烛图. T,技术分析,工具平台 L,公式Language语言使用c++14,Lite小巧简易. 项目仓库:https://github.com/bbqz007/KTL 国内仓库 ...
- oracle 11g rac修改监听端口(远程监听和本地监听)
转至:https://www.cnblogs.com/yj411511/p/12459533.html 目录 1.修改远程监听端口 1.1 查看远程监听状态 1.2 修改SCAN listener端口 ...
- selenium+python自动化101-使用execute_script() 方法获取 JavaScript 返回值
前言 之前经常使用 execute_script() 方法执行 JavaScript 的来解决页面上一些 selenium 无法操作的元素,但是一直无法获取执行的返回值. 最近翻文档,发现 execu ...