六种字符串匹配算法详解(含代码演示)

article/2025/9/21 12:57:55

1. Brute-Force算法
2. Rabin-Karp Hash算法
3. Kmp算法
4. Kmp的优化算法
5. Sunday算法
6. Shift-And算法

ps:字符串匹配其实是单模匹配问题

1.Brute-Force 朴素匹配算法(暴力匹配)

时间复杂度:O(n*m)

//返回 文本串s中第一次查找到模式串t的位置
int brute_force(const char *s, const char *t){//扫描文本串的每一位for(int i = 0; s[i]; i++){bool flag = true;//用当前的第i位和模式串向后比较for(int j = 0; t[j]; j++){if(s[i + j] == t[j]) continue;flag = false;break;}if(flag) return i;}return -1;
}

2.Rabin-Karp Hash匹配法

时间复杂度:O(n * m / P)

​ P最好取质数,则最大可以出现P-1种余数,冲突概率为1 / P。当P ~= m,时间复杂度 ~= O(n)

Hash值计算:
在这里插入图片描述

#include<stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>#define MAX_N 10000
#define DEFAULT_LEN 50
char s[MAX_N + 5], t[MAX_N + 5];#define TEST(func){\char temp_s[MAX_N + 5]; \sprintf(temp_s, "%s(\"%s\", \"%s\") = %3d\n", #func, s, t, func(s, t)); \int n = DEFAULT_LEN - strlen(temp_s); \while(n >= 0) n -= printf(" "); \printf("%s", temp_s); \
}typedef long long LL;int brute_one_match(const char *s, const char *t){printf("brute_one_match_called\n");for(int j = 0; t[j]; j++){if(s[j] == t[j]) continue;return 0;}return 1;
}//返回 文本串s中第一次查找到模式串t的位置
int brute_force(const char *s, const char *t){//扫描文本串的每一位for(int i = 0; s[i]; i++){if(brute_one_match(s + i, t)) return i;}return -1;
}//求a^b % c  快速幂
//把a进行二进制分解
int quick_mod(int a, int b, int c){int ans = 1;while(b){//b & 1 --> 取b二进制位的最后一位if(b & 1) ans = (LL)ans * a % c;b >>= 1; //去掉b二进制表示的最后一位a = (LL)a * a % c;}return ans;
}int hash_match(const char *s, const char *t){//base值也最好取一个质数。base取小质数,P取大质数int len = strlen(t), base= 31, P = 9973; //P = 1e9 + 7;int nbase = quick_mod(base, len, P); //base^len % Pint shash = 0, thash = 0;for(int i = 0; t[i]; i++) thash = (thash * base + t[i]) % P;for(int i = 0; s[i]; i++){shash = (shash * base + s[i]) % P;// + P 再 % P保证减完也是正数if(i >= len) shash  = (shash - (s[i - len] * nbase % P) + P) % P;if(i + 1 < len) continue; //所取的文本串s长度还 < 模式串t长度if(shash != thash) continue;//模式串中Hash值相等再按位比较,Hash值本身存在冲突//Hash值相等也不代表模式串匹配成功//按位比较(冲突的时候)if(brute_one_match(s + i - len + 1, t)) return i - len + 1;}return -1;
}int main(){while(scanf("%s%s", s, t)){//根据输出语句可以看出暴力匹配的次数 > hash匹配TEST(brute_force); TEST(hash_match);}return 0;
}

q1:两个不同的字符串拥有相同的哈希值怎么办?

**ans1:**正常比较即可

q2:算法的时间复杂度?

ans2: O(n * m / P)。合理选择P是hash匹配算法的关键

3.KMP算法

时间复杂度O(n + m)

NFA:不确定性有穷状态自动机

在这里插入图片描述

int *getNext(const char *t, int *len){*len = strlen(t);int *next = (int *) malloc (sizeof(int) * (*len)); // next[i] = j : 以i位置结尾,所匹配到的最长前缀是j这个 位置next[0] = -1; //匹配最长前缀不能包含自己,第一位的最长前缀为-1for(int i = 1, j = -1; t[i]; i++){//i是当前匹配的位置,j是i的前一位能匹配到的最长前缀的位置。所以i与j+1比较while(j != -1 && t[j + 1] != t[i]) j = next[j]; //匹配不成功就往前找if(t[j + 1] == t[i]) ++j; //匹配成功就按照i-1位置最长前缀的数量+1next[i] = j;//更新next数组}return next;
}int kmp(const char *s, const char *t){int len = 0;int *next = getNext(t, &len); //初始化next数组//j代表之前成功匹配过的模式串在第几位for(int i = 0, j = -1; s[i]; i++){ while(j != -1 && t[j + 1] != s[i]) j = next[j]; //NFA(不确定性)if(t[j + 1] == s[i]) ++j;if(t[j + 1] == '\0') { //完全成功匹配了模式串free(next);return i - len + 1;}}free(next);return -1;
}

4.KMP的优化:

DFA:确定性有穷状态自动机
在这里插入图片描述

void free_next(int *next){free(next);return ;
}void free_jump(int **jump, int len){for(int i = 0; i < len; i++) free(jump[i - 1]);free(jump - 1);return ;
}//获取跳转信息的数组
int **getJump(int *next, const char *t, int n){int **jump = (int **) malloc(sizeof(int *) * n);for(int i = 0; i < n; i++) jump[i] = (int *) malloc(sizeof(int) * 26);++jump; //让jump指向next数组1的位置,使它访问-1位置的时候实际上访问的是0,合法的for(int i = 0; i < 26; i++) jump[-1][i] = -1;jump[-1][t[0] - 'a'] = 0; //模式串的第一个字符for(int i = 0, I = n - 1; i < I; i++){for(int j = 0; j < 26; j++) jump[i][j] = jump[next[i]][j];jump[i][t[i + 1] - 'a'] = i + 1;}return jump;
}//Kmp的优化算法
int kmp_opt(const char *s, const char *t){int len = 0;int *next = getNext(t, &len);int **jump = getJump(next, t, len); //j代表之前成功匹配过的模式串在第几位for(int i = 0, j = -1; s[i]; i++){ j = jump[j][s[i] - 'a']; //DFA(确定性)if(j == len - 1){ //已找到字符串free_next(next);free_jump(jump, len);return i - len + 1;}}free_next(next);free_jump(jump, len);return -1;
}

4.Sunday算法

匹配效率比KMP高,只需统计每个字母最后一次出现的位置
在这里插入图片描述

int brute_one_match(const char *s, const char *t){printf("brute_one_match_called\n");for(int j = 0; t[j]; j++){if(s[j] == t[j]) continue;return 0;}return 1;
}int sunday(const char *s, const char *t){int tlen = strlen(t), slen = strlen(s);//计算每一种字符出现在字符串的倒数第几位int jump[128] = {0};//默认每个字符出现在模式串的-1位for(int i = 0; i < 128; i++) jump[i] = tlen + 1;//扫描模式串的每一位。t[i]字母出现在倒数第tlen-i位for(int i = 0; t[i]; i++) jump[t[i]] = tlen - i;for(int i = 0; i + tlen <= slen; ){//按位比较,判断当前匹配能否成功if(brute_one_match(s + i, t)) return i;//暴力匹配不成功,计算黄金对齐点位i += jump[s[i + tlen]];}return -1;
}

5.Shift-and算法

效率也比Kmp高
底层思维也是NFA
在这里插入图片描述在这里插入图片描述

int shift_end(const char *s, const char *t){int code[128] = {0}, n = 0;// |= :按位或赋值。x |= 2:计算按位或值2和变量x中的值,并使用按位或赋值运算符将结果分配回x//扫描模式串的每一位。将t[n]字符的第n位置为1for( ; t[n]; n++) code[t[n]] |= (1 << n);int p = 0;for(int i = 0; s[i]; i++){p = (p << 1 | 1) & code[s[i]];if(p & (1 << (n - 1))) return i - n + 1;//p的第n位为1,匹配成功,返回匹配成功的起始位置}return -1;
}

要是对您有帮助,点个赞再走吧~ 欢迎评论区讨论~


http://chatgpt.dhexx.cn/article/dBa1aBCm.shtml

相关文章

字符串匹配算法(BF、KMP)

BF算法 描述&#xff1a; BF&#xff0c;Brute Force&#xff0c;暴力匹配的意思&#xff0c;是最简单直观的字符串匹配算法。假设有主串s1和子串s2&#xff0c;根据BF算法判断s1是否包含s2的步骤如下&#xff1a; 初始下标指针 i, j 分别指向s1, s2的首位置&#xff0c;若s1…

这可能是全网最好的字符串匹配算法讲解

点击上方 好好学java &#xff0c;选择 星标 公众号重磅资讯&#xff0c;干货&#xff0c;第一时间送达 今日推荐&#xff1a;14 个 github 项目&#xff01;个人原创100W 访问量博客&#xff1a;点击前往&#xff0c;查看更多为保证代码严谨性&#xff0c;文中所有代码均在 le…

Spring boot 项目(五)——AOP切面

一、AOP简介 1、在软件业&#xff0c;AOP为Aspect Oriented Programming的缩写&#xff0c;意为&#xff1a;面向切面编程&#xff0c;通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 2、AOP是OOP的延续&#xff0c;是软件开发中的一个热点&#xff0c;也…

Spring AOP 切面(Aspect)应用详解

1. AOP 切面应用 下面是一个AOP切面的一个简单的应用实例 引入AOP依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>创建切面类对象 Aspect Component pub…

springboot实现AOP切面编程

概述 AOP(Aspect Oriented Programming) 即面向切面编程。面向切面是面向对象中的一种方式而已。在代码执行过程中&#xff0c;动态嵌入其他代码&#xff0c;叫做面向切面编程&#xff08;将交叉业务逻辑封装成成切面&#xff0c;利用AOP功能将切面织入到主业务逻辑———与主…

spring AOP切面及日志记录实现

目录 1.什么是AOP切面 2.理解AOP 3.AOP实例 1.自定义注解 2.创建一个切面类 3.将自定义注解标注在测试接口上 1.什么是AOP切面 AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff0c;面向切面思想&#xff0c;是Spring的三大核心思想之一。 在项目中经常…

Aop切面自定义注解的使用

一&#xff1a;功能简介 本文主要记录如何使用aop切面的方式来实现日志记录功能。 主要记录的信息有: 操作人&#xff0c;方法名&#xff0c;参数&#xff0c;运行时间&#xff0c;操作类型(增删改查)&#xff0c;详细描述&#xff0c;返回值。 二&#xff1a;项目结构图 三…

AOP切面注解

一.前言 在以前的项目中&#xff0c;很少去关注spring aop的具体实现与理论&#xff0c;只是简单了解了一下什么是aop具体怎么用&#xff0c;看到了一篇博文写得还不错&#xff0c;就转载来学习一下&#xff0c;博文地址&#xff1a;http://www.cnblogs.com/xrq730/p/4919025.h…

AOP切面执行顺序

文章目录 一. 概述二. 讲述1. 单切面中各通知方法的执行顺序2. 多切面中各通知方法的执行顺序3. 多切面的通知方法中抛出异常 参考资料 一. 概述 本文主要讲述以下几点 单AOP切面时&#xff0c;各通知方法的执行顺序。多AOP切面时&#xff0c;多切面的执行顺序和各通知方法的执…

spring aop切面执行顺序

spring aop切面执行顺序 切面执行顺序 现有切面1、切面2对同一个切点按先后顺序执行&#xff08;切面1先于切面2执行&#xff09; 切面1&#xff1a;Before、After、AfterReturnning、AfterThrowing、Around 执行前、Around 正常执行、Around 异常执行、Around 执行后切面2&am…

一文带你搞定AOP切面

摘要&#xff1a;AOP在spring中又叫“面向切面编程”&#xff0c;是对传统我们面向对象编程的一个补充&#xff0c;主要操作对象就是“ 切面”&#xff0c; 可以简单的理解它是贯穿于方法之中&#xff0c;在方法执行前、执行时、执行后、返回值后、异常后要执行的操作。 本文分…

SpringBoot AOP切面实现

文章目录 一、AOP简介二、AOP体系与概念三、AOP实例1、创建SpringBoot工程2、添加依赖3、AOP相关注解3.1、Aspect3.2、Pointcut3.2.1、execution()3.2.2、annotation() 3.3、Around3.4、Before3.5、After3.6、AfterReturning3.7、AfterThrowing 一、AOP简介 AOP&#xff08;As…

AOP切面编程的理解

一、什么是Spring的AOP&#xff1f; AOP在spring中又叫“面向切面编程”&#xff0c;它可以说是对传统我们面向对象编程的一个补充&#xff0c;从字面上顾名思义就可以知道&#xff0c;它的主要操作对象就是“切面”&#xff0c;所以我们就可以简单的理解它是贯穿于方法之中&a…

AOP切面使用

一、主要设计注解&#xff1a; Aspect After before Pointcut Around pom文件引入 <!--用于aop切面编程--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency> 二、AOP核心…

AOP面向切面

1.什么是Spring的AOP? AOP又叫"面向切面编程",是对传统的面向对象编程的一个补充,主要的操作对象就是"切面 ",可以简单的理解它是贯穿于方法之中,在方法执行前、执行时、执行后、返回值后、异常后要执行的操作。 相当于将我们原本一条线执行的程序在中间切…

【JavaEE】Spring AOP (面向切面)详解

目录&#xff1a; 1. 什么是 Spring AOP&#xff1f;1.1 AOP1.2 使用 AOP 的场景 2. AOP 组成2.1 切面&#xff08;Aspect&#xff09;2.2 连接点&#xff08;Join Point&#xff09;2.3 切点&#xff08;Pointcut&#xff09;2.4 通知&#xff08;Advice&#xff09; 3. AOP 概…

斜杠,反斜杠说明

/ 斜杠 \反斜杠 在window中都用斜杠 反斜杠是用来转译字符串的 eg: \"a\" 输出"a"

斜杠、反斜杠、双斜杠、反双斜杠的区别和使用方法及范围

背景 这边我就找了两篇大神写的文章&#xff0c;讲得非常清晰明了。文章主要讲了一些历史缘故和我们面对各种斜杠时的疑惑。 斜杠’/’ 和反斜杠’’ 深入探讨正斜杠和反斜杠 概念 1. 斜杠"/"是URL地址中用到的分隔符&#xff0c;并且在linux系统中的文件路径也是…

glob.glob()之返回路径的正反斜杆问题

Windows环境下用一个反斜杠就行 绝对路径&#xff1a;D:\PyCharm_code\pytorch_study\xxx 相对路径&#xff1a;.\cifar10_train\**\*.png 以下是踩过的坑&#xff1a;记录下

正反斜杠的区别_正斜杠( / )和反斜杠( \ )的区别

反斜杠“\”是电脑出现了之后为了表示程序设计里的特殊含义才发明的专用标点。所以除了程序设计领域外&#xff0c;任何地方都不应该使用反斜杠。 如何区分正反斜杠 英语&#xff1a;"/" 英文是forward slash, “\" 是backward slash 形象些比喻的话&#xff0c…