这是两个关于字符串的查找匹配算法:
字符串:
char*a是链表式字符存储结构,char a[10]是顺序表式存储结构
BF(Brute Force)暴力匹配算法:例,字符串S,T,先匹配S[0],T[0],若想等则向后比较S[1],T[1],
否则比较S[0]T[1],T依次向后移,直到匹配成功后,S的索引再向后移一位(回溯路线)
KMP算法(字符串匹配的算法),由三个人的名字来进行命名的,从最后一个不相同元素的索引上进行匹配
情况一:当字符间没有重复的话,把首地址放到首先不匹配的字符索引上
情况二:如果单个字符有重复相等的,则移动一格进行匹配检验
情况三:如果字符首前缀(首地址相等的字符块)和后缀(匹配的字符前面的字符) 的字符数相等的话,将首地址放到下一块相等的字符索引上
前缀与后缀的字符可以重复,但不能在首字符的索引上相重合
NEXT数组:当模式匹配串T失配时,NEXT数组对应的元素指导应用T串的哪个元素进行下一轮的匹配
KMP算法的关键是求出匹配子串所对应的NEXT数组(原子串对应下面的前缀和后缀相等的数目
遇到相等的情况就退而求全之,前缀不相等时填0(0表示数组中字符的数量),前缀后缀不等时填1,前后缀有一个字符数相等时填2
一个改进KMP算法的例子:
PS:(神TM boy friend算法 与 看毛片算法,在网易云上被某鱼鱼忽悠了,他的那个改进算法码的有误,判断下一个字符相同时需要在NEXT数组的下标不为0的情况下。还有,在最开始的字符比较如果不同时,子串会返回-1,需要把子串复原,主串加1)
以下代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
//Brute Force
int BF(char a[], char b[]) {
//字符串变量不能用strlen()求出它里的字符的数量,如果是复制一份数组过来的话,就可以求数量了
int alen = strlen(a);
int blen = strlen(b);
int i = 0, j = 0;
while (i < alen&&j < blen) {
if (a[i] == b[j]) {
i++;
j++;
}
else {
i = i - j + 1;
j = 0;
}
}
if (j == blen)
return i - j + 1;
else
return -1;
}
//KMP算法
void getNext(char a[], int *next) {//得到匹配子串的next数组
int alen = strlen(a);
int j = -1, i = 0;
next[i] = j;
while (i < alen) {
if (j == -1 || a[j] == a[i]) {
j++;
i++;
if (a[j] == a[i] && j != 0) {
next[i] = next[j];
//前缀是数组前面一串字符,后缀是后面与前缀相匹配的一串字符,它们的数量是统一的
// 在这里如果j!=0&&数组目前的字符与前缀相等时,可以把其视为一个字符块,
//当其后缀不相同时,与他相同的前缀也没必要进行检验了
}
else
next[i] = j;//当其移动后的字符不同时或j=0时,应该从最开始不相同的那个前缀后的那个下标开始匹配
}
else//如果字符不相同时则回溯到上一次前缀不同时的下标进行重新匹配
j = next[j];
//next数组是让匹配子串自己搞自己,在整个过程通过j来回溯前缀,其后缀是不断改变的
}
}
int KMP(char a[], char b[]) {
int alen = strlen(a);
int blen = strlen(b);
int next[100] = { 0 };
getNext(b, next);
//给申请的next数组赋值
int i = 0, j = 0;
while (i < alen && j < blen) {
//这里一定要用&&
if (a[i] == b[j]) {
i++;
j++;
}
else
j = next[j];//只回溯匹配子串的下标即可
//忽略对主串下标的操作
if (j == -1) {
//在开始的字符比较如果不同时,j会返回-1,此时要把主串加1,子串复原
j=0;
i++;
}
}
if (j == blen) {
return i - j + 1;
}
else
return -1;
}
int main() {
clock_t start, over,over2;
//char a[] = "hellomy roo roijjk34jkrolleharrolleharolleolldmfejfndmg,a d,fnb,sdnflij4jdhgnijj j997867yroo roolbrotheroller";
srand(time(0));
char a[1000020] = { 0 };
int i;
for (i = 0; i < 1000000; i++) {
a[i] = rand() % 220 + 1;//用随机数来给a赋值
}
char b[] = "Mikuisletsyitddddd";
strcat(a, b);//把b拼到a上,并让a的最后一个字符为0
start = clock();
int result1 = BF(a, b);
over = clock();
int time1 = over - start;
int result2 = KMP(a, b);
over2= clock();
int time2 = over2 - over;
//数组就是指针,可以直接引用
if (result1 == -1 || result2 == -1) {
printf("未查找到子串!\n");
}
else {
printf("BF查找到子串的索引为:%d,用时:%d毫秒\nKMP查找到子串的索引为:%d,用时:%d毫秒\n", result1, time1, result2, time2);
}
return 0;
}
用了随机数,做了一下测试,发现效果不明显