首页 > 其他分享 >字符串匹配|kmp笔记

字符串匹配|kmp笔记

时间:2023-05-29 13:44:26浏览次数:43  
标签:nxt 匹配 abcabd ++ abcabcabcabd pnt 笔记 kmp 字符串

很久之前学的了。

做个笔记回忆一下:

kmp

朴素比对字符串

所谓字符串匹配,是这样一种问题:“字符串 T 是否为字符串 S 的子串?如果是,它出现在 S 的哪些位置?” 其中 S 称为主串;T 称为模式串。如在字符串s abcabcabcabd 中找到子串T abcabd :

先设两个指针i、j,i表示S的指针,j表示T的指针

i=j=0
↓(i)
abcabcabcabd
abcabd
↑(j)

匹配成功,移动指针(i++,j++)

 ↓
abcabcabcabd
abcabd
 ↑

匹配成功,移动指针(i++,j++)

.
.
.

     ↓
abcabcabcabd
abcabd
     ↑

c≠d,回溯(i=1,j=0)

 ↓
abcabcabcabd
abcabd
↑

b≠a,回溯(i=2,j=0)

.
.
.

      ↓
abcabcabcabd
abcabd
↑

匹配成功,移动指针(i++,j++)

       ↓
abcabcabcabd
abcabd
 ↑

匹配成功,移动指针(i++,j++)

        ↓
abcabcabcabd
abcabd
  ↑

.
.
.

           ↓
abcabcabcabd
abcabd
     ↑

匹配成功,找到模式串(print(i))

优化

上面的复杂度是 O(nm) ,为什么这么多,发现是回溯花费时间过多。我们合理的希望是i不回溯,即:

先设两个指针i、j,i表示S的指针,j表示T的指针

i=j=0
↓(i)
abcabcabcabd
abcabd
↑(j)

匹配成功,移动指针(i++,j++)

 ↓
abcabcabcabd
abcabd
 ↑

匹配成功,移动指针(i++,j++)

.
.
.

     ↓
abcabcabcabd
abcabd
     ↑

c≠d,i不回溯(j=2)

     ↓
abcabcabcabd
abcabd
  ↑

匹配成功,移动指针(i++,j++)

      ↓
abcabcabcabd
abcabd
   ↑

匹配成功,移动指针(i++,j++)

       ↓
abcabcabcabd
abcabd
    ↑

匹配成功,移动指针(i++,j++)

        ↓
abcabcabcabd
abcabd
     ↑

a≠d,i不回溯(j=2)

        ↓
abcabcabcabd
abcabd
  ↑

匹配成功,移动指针(i++,j++)

.
.
.

           ↓
abcabcabcabd
abcabd
     ↑

匹配成功,找到模式串(print(i))

全程i不会减少

nxt数组

我们假设知道一个叫做nxt的数组,代表下一个j,当匹配失败时就可以 j=nxt[j] 来防止i的回溯。那么我们可以快速算出他的子串,如下代码:

int KMP(){
	for(int i=0,j=0;i<n;i++){
		while(j>0 && str[i]!=pnt[j]){
			j=nxt[j-1];      // 为什么是 nxt[j-1],这里埋一个伏笔
		}
		if(str[i]==pnt[j]){
			j++;             // 匹配成功
		}
		if(j==m){                // 匹配成功
			return i-j+1;
		}
	}
	return -1;
}

nxt数组是什么

nxt代表重复真子集长度,和回文串差不多,但不是回文串。区别

回文串:abccba
重复真子集:abcabc

欸,那么我们可以看出当已经有abc不匹配:

     ↓
abcabdabcabc
abcabc
     ↑

我们可以快速跳到重复真子集中与他重复的部分:

     ↓
abcabdabcabc
abcabc
  ↑(虽然c也不等于d)

我们nxt储存的就是与它重复的这部分的位置。以 abcababdabc 为例:

a:0(因为是真子集,不包括自身)
ab:0
abc:0
abca:1
abcab:2
abcaba:1
abcabab:2
abcababd:0
abcababda:1
abcababdab:2
abcababdabc:3

那么我们会发现,他们重复这部分的下标(以0开始)刚好就是重复真子集长度:

abcab为例,abcab的重复真子集长度为2,而且ab也是重复的,所以我们只需要匹配下标为2的c即可。j=nxt[5]

计算nxt数组

我们可以用递推的思想,先设有nxt[0]=0(必然的),然后设有快指针i=1,慢指针j=0,刚好,我们会发现重复部分的长度也是j的值。

对于匹配成功,则j++

对于匹配失败,则从上一位nxt中找到重复部分回溯j。

代码如下:

void makeNext(){
	nxt[0]=0;
	for(int i=1,j=0;i<m;i++){
		while(j>0 && pnt[i]!=pnt[j]){
			j=nxt[j-1];    // 因为nxt表示重复部分的下标,我们可以回溯回去
		}
		if(pnt[i]==pnt[j]){
			j++;
		}
		nxt[i]=j;
	}
}

为什么kmp代码中j=nxt[j-1]

是指kmp()中的而不是makeNext()中的,因为第j位和第i位已经不匹配了,j-1位和i-1位才是匹配的,所以用j=nxt[j-1]

代码:

#include<cstdio> 
#include<cstring> 
#include<string>
char str[1010],pnt[1010];
int n,m;
int nxt[1010]; 
void makeNext(){
	nxt[0]=0;
	for(int i=1,j=0;i<m;i++){
		while(j>0 && pnt[i]!=pnt[j]){
			j=nxt[j-1];
		}
		if(pnt[i]==pnt[j]){
			j++;
		}
		nxt[i]=j;
	}
}
int KMP(){
	for(int i=0,j=0;i<n;i++){
		while(j>0 && str[i]!=pnt[j]){
			j=nxt[j-1];
		}
		if(str[i]==pnt[j]){
			j++;
		}
		if(j==m){
			return i-j+1;
		}
	}
	return -1;
}
int main(){
	scanf("%s %s",str,pnt);
	n=strlen(str);
	m=strlen(pnt);
	makeNext();
	printf("%d",KMP());
} 

标签:nxt,匹配,abcabd,++,abcabcabcabd,pnt,笔记,kmp,字符串
From: https://www.cnblogs.com/znpdco/p/17440146.html

相关文章

  • KMP算法
    就我学过的所有处理字符串的算法(包括匹配算法、回文算法、后缀算法、字符串哈希),都离不开两个恒定的主题:递推构建和压缩信息。这一特征很明显和字符串的性质有关:子串众多,而子串之间互相关联性强。字符串的算法大多数都是\(O(n)\)的时间或空间复杂度,和“字符串本身包含的信息只有......
  • 深入虚拟机笔记之整数运算
    第12章整数运算     二进制补码运算:java虚拟机支持的所有整数类型:byte、short、int、long,它们都是带符号的二进制补码数。在一个二进制补码数中,最重要的位是它的符号位(最高位),0表示正整数和0,1表示负整数。   能够被二进制补码表示的数值范围为:2的总位数的次幂。其中一半是......
  • 深入虚拟机笔记之类型的生命周期
    第7章类型的生命周期        java虚拟机通过装载、连接和初始化一个java类型,使该类型可以被正在运行的java程序所使用。   装载:是把二进制形式的java类型读入java虚拟机中。   连接:是把读入的二进制形式的类型数据合并到虚拟机的运行时状态中去。连接分三个子步......
  • 深入虚拟机笔记之整数运算
    第12章整数运算     二进制补码运算:java虚拟机支持的所有整数类型:byte、short、int、long,它们都是带符号的二进制补码数。在一个二进制补码数中,最重要的位是它的符号位(最高位),0表示正整数和0,1表示负整数。   能够被二进制补码表示的数值范围为:2的总位数的次幂。其中一半是......
  • go语言字符串相关
    字符串使用双引号或反引号引起来的任意个字符。它是字面常量。注意,反引号内不支持转义字符。"abc测试"//不能换行,换行需要借助\n"abc\n测试"//换行`abc测试`//等价下面的字符串"abc\n\t测试"`json:"name"`//字符串里面如果有双引号,使用反引号定义方便"json:\"n......
  • 哈希处理字符串匹配
    问题A:【哈希和哈希表】子串查找时间限制:1Sec  内存限制:128MB提交:65  解决:18[提交][状态][讨论版][命题人:admin]题目描述这是一道模板题。给定一个字符串A和一个字符串B,求B在A中的出现次数。A和B中的字符均为英语大写字母或小写字母。A中不同位置出现的B......
  • MySQL 将 字符串 转为 整数
    1、CAST(eprAStype)1)type为 SIGNEDSELECTCAST("-12"ASSIGNED);效果如下:2)type为UNSIGNEDSELECTCAST("-12"ASUNSIGNED);效果如下:2、CONVERT(expr,type)SELECTCONVERT('123',SIGNED);额外补充1、CAST和CONVERT两个函数中的type取值可以为:SIGNED,UNS......
  • upc 6597: Don't Be a Subsequence (字符串的最短不匹配子序列 dp)
    6597:Don'tBeaSubsequence时间限制:1Sec  内存限制:128MB提交:237  解决:45[提交][状态][讨论版][命题人:admin] 题目描述AsubsequenceofastringSisastringthatcanbeobtainedbydeletingzeroormorecharactersfromSwithoutchangingtheor......
  • 开发 Java笔记
    1.Controller@RequestMapping注解用于绑定URI到具体处理器。@RestController:Spring4新增注解,同样可以注解Controller类,相当于@Controller+@ResponseBody,主要是为了使http请求返回 json 或者xml格式数据,一般情况下都是使用这个注解。下文都基于此注解进行验证。用于将......
  • 二进制数据与16进制字符串相互转化方法
    二进制数据转化为16进制字符串(中间加的‘:'还有‘;'是为了查看下标,也可以自行去掉):publicstaticStringbytesToHexString(byte[]src){StringBuilderstringBuilder=newStringBuilder();if(src==null||src.length<=0){returnnull;}for(inti=0;i<src.length;......