1.模式匹配算法概览
1.1算法目的:
确定主串中所含模式串第一次出现的位置(即定位操作)
1.2算法种类
- BF算法(Brute-Force,又称古典的、经典的、朴素的、穷举的)
- KMP算法(特点:速度快)
1.3子串与模式串的区别
子串:主串的一部分,一定存在
模式串:不一定能在主串中找到
2.BF------简单匹配算法(实际上就是暴力查找)
int BFindex(sstring s, sstring t)//可以从主串的第一个位置开始匹配
int BFindex(sstring s, sstring t,int pos)//也可以从主串的第任意位置开始匹配
第二种方法只需要将i初始化为传入的pos即可(i:主串的第一个匹配位置),其余部分与第一种方法完全一致,本篇中不再过多赘述
2.1算法的核心思路与核心步骤:
2.1.1核心思路:设主串长度为n,模式串长度为m
将主串中所有长度为m的子串(最多为n-m+1个子串)依次与模式串对比,直到找到完全匹配的子串或所有的子串都不匹配为止。
2.1.2 核心步骤:
- <1>字符匹配失败时,重置匹配位置并重新开始比较
注意主串重置的位置为当前匹配起始位置的下一个位置
公式为:i-j+2
实例图解:
按照这种滑动匹配的方式,一直到匹配成功
- <2>匹配成功时,返回主串中模式串的首个字符下标
公式为:i-t.length
比如上述图解中,匹配成功时,j=7,i=10,返回i-t.length=10-6=4
如下图:
2.2时间复杂度分析:O(nm)
最坏的情况,每个子串都要对比 m 个字符,共 n-m+1个子串
复杂度=0((n-m+1)m)=0(nm)
注:很多时候,n远远大于m
2.3算法步骤:
(1)初始化匹配位置(注意是否是从下标1开始存起)
(2)逐字符比较主串和模式串中的字符
终止条件:i <= s.length && j <= t.length
<1> 若当前字符匹配成功,则比较下一个字符
<2> 若当前字符匹配失败,重置匹配位置并重新开始比较
公式:i=i-j+2
(3)判断匹配结果
<1>匹配成功返回主串中模式串的首个字符位置
判断条件:j>t.length
公式:i-t.length
<2>匹配失败,返回 0
#include<stdio.h>
#include<stdlib.h>
#define bool int
#define true 1
#define false 0
#define MAXSIZE 255//预定义最大串长为255
//1.串类型定义(静态数组)
typedef struct
{
char ch[MAXSIZE + 1];//每个空间存储一个字符
//这里加1的目的是,串的有效元素要从下标1开始存起,方便管理,长度最大仍然为255
int length;//串的实际长度
}sstring;
//sstring:结构体别名,用来定义串
// 串的简单匹配算法——BF (Brute Force)
// 传入主串 s 和模式串 t,返回匹配成功的第一个位置,未匹配返回 0
int BFindex(sstring s, sstring t)
{
// [1] 初始化匹配位置
// i 初始化为主串的第一个字符位置,j 初始化为模式串的第一个字符位置
int i = 1, j = 1;
// [2] 逐字符比较主串和模式串中的字符
while (i <= s.length && j <= t.length)
// 当 i > s.length 时,表示主串已比较完毕
// 当 j > t.length 时,表示模式串已比较完毕
// 此处用 <= 是因为下标从 1 开始,确保不会越界
{
// <1> 若当前字符匹配成功,则比较下一个字符
if (s.ch[i] == t.ch[j])
{
i++; // i 移动到主串的下一个字符
j++; // j 移动到模式串的下一个字符
}
// <2> 若当前字符匹配失败,重置匹配位置并重新开始比较
else
{
// <核心1> 主串的匹配位置重置为当前匹配起始位置的下一个位置
i = i - j + 2;
// 模式串的匹配位置重置为第一个字符
j = 1;
}
}
// [3] 判断匹配结果
if (j > t.length)
// 匹配成功,返回主串中模式串的首个字符位置
// j > t.length 表示模式串已经完全匹配,j 此时等于 t.length + 1
{
//<核心2>返回主串中模式串的首个字符位置
return i - t.length;
}
else
{
// 匹配失败,返回 0
return 0;
}
}
int main()
{
// 初始化主串和模式串
sstring s1 = { "#abcdef", 6 }; // 主串: "abcdef",长度为6
sstring t1 = { "#cde", 3 }; // 模式串: "cde",长度为3
sstring s2 = { "#aaaaaab", 7 }; // 主串: "aaaaaab",长度为7
sstring t2 = { "#aab", 3 }; // 模式串: "aab",长度为3
sstring s3 = { "#abcd", 4 }; // 主串: "abcd",长度为4
sstring t3 = { "#efg", 3 }; // 模式串: "efg",长度为3
sstring s4 = { "#aaaaa", 5 }; // 主串: "aaaaa",长度为5
sstring t4 = { "#aaaaaa", 6 }; // 模式串: "aaaaaa",长度为6
sstring s5 = { "#abcabcabc", 9 }; // 主串: "abcabcabc",长度为9
sstring t5 = { "#abc", 3 }; // 模式串: "abc",长度为3
// 执行测试用例
printf("测试用例 1: 预期 3, 结果 %d\n", BFindex(s1, t1));
printf("测试用例 2: 预期 5, 结果 %d\n", BFindex(s2, t2));
printf("测试用例 3: 预期 0, 结果 %d\n", BFindex(s3, t3));
printf("测试用例 4: 预期 0, 结果 %d\n", BFindex(s4, t4));
printf("测试用例 5: 预期 1, 结果 %d\n", BFindex(s5, t5));
return 0;
}
2.4补录:当有效字符从下标为零处开始存储时
1.算法步骤的差异:
(1)初始化匹配位置(注意是从下标0开始存起)
(2)逐字符比较主串和模式串中的字符
终止条件:i < s.length && j < t.length
<1> 若当前字符匹配成功,则比较下一个字符
<2> 若当前字符匹配失败,重置匹配位置并重新开始比较
公式:i=i-j+1
(3)判断匹配结果
<1>匹配成功返回主串中模式串的首个字符位置
判断条件:j>t.length-1
公式:i-t.length(此条件不变)
<2>匹配失败,返回 0
除了返回位序时的操作不变外,其余操作都需要调整
图解如下
#include<stdio.h>
#include<stdlib.h>
#define bool int
#define true 1
#define false 0
#define MAXSIZE 255//预定义最大串长为255
//1.串类型定义(静态数组)
typedef struct
{
char ch[MAXSIZE];//每个空间存储一个字符
int length;//串的实际长度
}sstring;
//sstring:结构体别名,用来定义串
// 串的简单匹配算法——BF (Brute Force)(下标为0开始存)
// 传入主串 s 和模式串 t,返回匹配成功的第一个位置,未匹配返回 0
int BFindex(sstring s, sstring t)
{
// [1] 初始化匹配位置
// i 初始化为主串的第一个字符位置,j 初始化为模式串的第一个字符位置
int i = 0, j = 0;
// [2] 逐字符比较主串和模式串中的字符
while (i < s.length && j < t.length)
// 当 i == s.length 时,表示主串已比较完毕
// 当 j == t.length 时,表示模式串已比较完毕
// 此处用 < 是因为下标从 0 开始,确保不会越界
{
// <1> 若当前字符匹配成功,则比较下一个字符
if (s.ch[i] == t.ch[j])
{
i++; // i 移动到主串的下一个字符
j++; // j 移动到模式串的下一个字符
}
// <2> 若当前字符匹配失败,重置匹配位置并重新开始比较
else
{
// <核心1> 主串的匹配位置重置为当前匹配起始位置的下一个位置
i = i - j + 1;
// 模式串的匹配位置重置为第一个字符
j = 0;
}
}
// [3] 判断匹配结果
if (j > t.length-1)
// 匹配成功,返回主串中模式串的首个字符位置
// j == t.length 表示模式串已经完全匹配
{
//<核心2>返回主串中模式串的首个字符位置
return i - t.length;//此操作与下标为1开始存操作一致
}
else
{
// 匹配失败,返回 -1(因为0此时为有效下标)
return -1;
}
}
int main()
{
// 初始化主串和模式串
sstring s1 = { "abcdef", 6 }; // 主串: "abcdef",长度为6
sstring t1 = { "cde", 3 }; // 模式串: "cde",长度为3
sstring s2 = { "aaaaaab", 7 }; // 主串: "aaaaaab",长度为7
sstring t2 = { "aab", 3 }; // 模式串: "aab",长度为3
sstring s3 = { "abcd", 4 }; // 主串: "abcd",长度为4
sstring t3 = { "efg", 3 }; // 模式串: "efg",长度为3
sstring s4 = { "aaaaa", 5 }; // 主串: "aaaaa",长度为5
sstring t4 = { "aaaaaa", 6 }; // 模式串: "aaaaaa",长度为6
sstring s5 = { "abcabcabc", 9 }; // 主串: "abcabcabc",长度为9
sstring t5 = { "abc", 3 }; // 模式串: "abc",长度为3
// 执行测试用例
printf("测试用例 1: 预期 2, 结果 %d\n", BFindex(s1, t1));
printf("测试用例 2: 预期 4, 结果 %d\n", BFindex(s2, t2));
printf("测试用例 3: 预期 -1, 结果 %d\n", BFindex(s3, t3));
printf("测试用例 4: 预期 -1, 结果 %d\n", BFindex(s4, t4));
printf("测试用例 5: 预期 0, 结果 %d\n", BFindex(s5, t5));
return 0;
}
3.KMP------(旧版看猫片算法,O(∩_∩)O)
暂时没学会,等以后学会了再更新