本文共 7665 字,大约阅读时间需要 25 分钟。
子串的定位操作是找子串在主串中从第pos个字符后首次出现的位置,又被称为串的模式匹配
BF算法思想:Brute-Force算法又称蛮力匹配算法(简称BF算法),从主串S的第pos个字符开始,和模式串T的第一个字符进行比较,若相等,则继续比较后续字符;否则回溯到主串S的第pos+1个字符开始重新和模式串T进行比较。以此类推,直至模式串T中的每一个字符依次和主串S中的一个连续的字符序列相等,则称模式匹配成功,此时返回模式串T的第一个字符在主串S中的位置;否则主串中没有和模式串相等的字符序列,称模式匹配不成功。
BF算法思想:
从主串S的第pos个字符开始的子串与模式串T比较的策略是从前到后依次进行比较。因此在主串中设置指示器i表示主串S中当前比较的字符;在模式串T中设置指示器j表示模式串T中当前比较的字符。实例分析:
给定主串为S=“ababcabcacbab”,T=“abcac”。匹配过程如下实现代码:
//BF模式匹配算法int Index(HString S, int pos, HString T) { int i = pos;//主串从pos开始 int j = 1;//模式串从头开始 while (i <= S.len&&j <= T.len) { if (S.ch[i] == T.ch[j]) { //当对应字符相等时,比较后续字符 i++; j++; } else { //当对应字符不相等时 i = i - j + 2;//主串回溯到i-j+2的位置重新比较 j = 1;//模式串从头开始重新比较 } } if (j > T.len) { //匹配成功时,返回匹配起始位置 return i - T.len; } else { //匹配失败,返回0 return 0; }}
void Get_Next(HString T, int next[]) { int j = 1, k = 0; next[1] = 0; while (j < T.len) { if (k == 0 || T.ch[j] == T.ch[k]) { ++j; ++k; next[j] = k; } else { k = next[k]; } }}
我们在前面介绍的next函数虽然好用,但还不够完美,在某种情况下是有缺陷的,例如如下匹配过程
主串为"aaabaaaab" 模式串为"aaaab"在求得模式串的next值之后,匹配过程如下:
从串的匹配过程可以看到,当i=4,j=4时,S4!=T4,由next[j]所示还需进行i=4、j=3,i=4、j=2,i=4、j=1这三次比较。实际上,因为模式串中的第1、2、3个字符和第4个字符都相等(即都是’a’),因此,不需要再和主串中第4个字符相比较,而可以直接将模式串一次向右滑动4个字符的位置直接进行i=5、j=1的判断。 这就是说,若按上述定义得到next[j]=k,而模式串中T(j)!=T(k),则当Si != Tj时,不需要进行Si和Tk的比较,直接和Tnext[k]进行比较;换句话说,此时的next[j]的值应和next[k]相同,为此将next[j]修正为nextval[j]。而模式串中Tj !=Tk,则当Si !=Tj时,还是需要比较Si和Tk的比较,因此nextval[j]的值就是k,即nextval[j]得值就是next[j]的值。 通过以上分析,计算第j个字符得nextval值时,要看第j个字符是否和第j个字符的next值指向的字符相等。若相等,则nextval[j]=nextval[next[j]];否则,nextval[j]=next[j]。 由此推导出模式串T='aaaab’的nextval值的计算过程如下:void Get_Next(HString T, int next[]) { int j = 1, k = 0; next[1] = 0; while (j < T.len) { if (k == 0 || T.ch[j] == T.ch[k]) { ++j; ++k; next[j] = k; } else { k = next[k]; } }}void Get_NextVal(HString T, int next[], int nextval[]) { int j = 2, k = 0; Get_Next(T, next); nextval[1] = 0; while (j <= T.len) { k = next[j]; if (T.ch[j] == T.ch[k]) { nextval[j] = nextval[k]; } else { nextval[j] = next[j]; } j++; }}
通常,模式串的长度m比主串的长度n小很多,且计算next或者nextval函数的时间复杂度为O(m)。因此,对于整个匹配算法来说,所增加的计算next或nextval时值得的。 BF算法的时间复杂度为O(m*n),但是实际接近于O(n+m),因此至今仍被采用。KMP算法仅当模式串与主串之间存在许多“部分匹配”的情况下,才会比BF算法快。KMP算法的最大特点就是主串的指针不需要回溯,整个匹配过程中,主串仅需从头到尾扫描一次,对于处理从外设输入的庞大文件很有效,可以边读边匹配。
头文件:DString.h
#pragma once#includetypedef struct { //堆串存储结构 char *ch;//若是非空串,则是指向串的起始地址;否则为空 int len;//字符串的长度}HString;//串初始化函数void HStrInit(HString *S) { S->ch = NULL; S->len = 0;}//串赋值函数int HStrAssign(HString *S, const char *chars) { int i = 0; if (NULL == chars || NULL == S) { return 0; } else { while (chars[i] != '\0') { i++; } S->len = i;//串S的长度等于串chars的长度 if (0 != S->len) { if (S->ch != NULL) { free(S->ch); } S->ch = (char *)malloc(sizeof(char)*(S->len + 1));//0号单元不用,故比实际需求多开辟一个空间 if (NULL == S->ch) { //空间开辟失败 return 0; } for (i = 1; i <= S->len; i++) { //将chars的内容逐一赋值给S->ch S->ch[i] = chars[i - 1]; } } else { S->ch = NULL;//当chars的长度为0时,则置S为空串。 } return 1; }}//打印void print(HString *S) { for (int i = 1; i <= S->len; i++) { printf("%c", S->ch[i]); } printf("\n");}//BF模式匹配算法int Index(HString S, int pos, HString T) { int i = pos;//主串从pos开始 int j = 1;//模式串从头开始 while (i <= S.len&&j <= T.len) { if (S.ch[i] == T.ch[j]) { //当对应字符相等时,比较后续字符 i++; j++; } else { //当对应字符不相等时 i = i - j + 2;//主串回溯到i-j+2的位置重新比较 j = 1;//模式串从头开始重新比较 } } if (j > T.len) { //匹配成功时,返回匹配起始位置 return i - T.len; } else { //匹配失败,返回0 return 0; }}//统计BF模式匹配算法的比较次数int IndexCount(HString S, int pos, HString T) { int i = pos;//主串从pos开始 int j = 1;//模式串从头开始 int count = 0; while (i <= S.len&&j <= T.len) { if (S.ch[i] == T.ch[j]) { //当对应字符相等时,比较后续字符 i++; j++; } else { //当对应字符不相等时 i = i - j + 2;//主串回溯到i-j+2的位置重新比较 j = 1;//模式串从头开始重新比较 } count++; } return count; }//KMP模式匹配算法void Get_Next(HString T, int next[]) { int j = 1, k = 0; next[1] = 0; while (j < T.len) { if (k == 0 || T.ch[j] == T.ch[k]) { ++j; ++k; next[j] = k; } else { k = next[k]; } }}void Get_NextVal(HString T, int next[], int nextval[]) { int j = 2, k = 0; Get_Next(T, next); nextval[1] = 0; while (j <= T.len) { k = next[j]; if (T.ch[j] == T.ch[k]) { nextval[j] = nextval[k]; } else { nextval[j] = next[j]; } j++; }}int Index_KMP(HString S, int pos, HString T,int nextval[]) { int i = pos;//主串从第pos开始 int j = 1;//模式串从头开始 while (i <= S.len&&j <= T.len) { if (j == 0 || S.ch[i] == T.ch[j]) { //继续比较后继字符 ++i; ++j; } else { j = nextval[j]; } } if (j > T.len) { //匹配成功 return i - T.len;//返回匹配的起始位置 } else { return 0; }}//统计KMP算法的比较次数int Index_KMPCount(HString S, int pos, HString T, int nextval[]) { int i = pos;//主串从第pos开始 int j = 1;//模式串从头开始 int count = 0; while (i <= S.len&&j <= T.len) { if (j == 0 || S.ch[i] == T.ch[j]) { //继续比较后继字符 ++i; ++j; } else { j = nextval[j]; } count++; } return count;}
源文件:
#include#include #include #include"DString.h"int main() { HString S; HString T; HStrInit(&S); HStrInit(&T); char a[] = "aaabaaaab"; char b[] = "aaaab"; HStrAssign(&S, a); HStrAssign(&T, b); printf("主串:"); print(&S); printf("模式串:"); print(&T); printf("BF模式匹配:\n"); int index = Index(S, 1, T); int count1 = IndexCount(S, 1, T); printf("index=%d\n", index); printf("比较次数:%d\n", count1); printf("KMP模式匹配:\n"); int *next,*nextval; next = (int *)malloc(sizeof(int)*(T.len + 1)); nextval = (int *)malloc(sizeof(int)*(T.len + 1)); Get_NextVal(T,next,nextval); printf("next[]数组如下\n"); for (int i = 1; i < (T.len + 1); i++) { printf("%d ", next[i]); } printf("\nnextval[]数组如下:\n"); for (int i = 1; i < (T.len + 1); i++) { printf("%d ", nextval[i]); } printf("\n"); int index1 = Index_KMP(S, 1, T, next); int count2 = Index_KMPCount(S, 1, T, nextval); printf("index1=%d\n", index1); printf("比较次数:%d\n",count2); system("pause"); return 0;}
转载地址:http://fnzzi.baihongyu.com/