BF算法
int BF(char A[], char B[]){
int i = 0, j = 0;
while(A[i] != '\0' && B[j] != '\0'){
if(A[i] == B[j]){
i++;
j++;
}else{
i = i - j + 1;
j = 0;
}
}
if(B[j] == '\0'){
return i - j + 1;
}else{
return -1;
}
}
KMP算法:
int KMP(char A[], char B[]){
int i = 0, j = 0;
int next[80];
GetNext1(B, next);
while(A[i] != '\0' && B[j] != '\0'){
if(A[i] == B[j]){
i++;
j++;
}else{
j = next[j];
if(j == -1){
i++;
j++;
}
}
}
if(B[j] != '\0'){
return -1;
}else{
return (i - j + 1);
}
}
KMP算法获取next[]值得两种方法:
方法一:
void GetNext(char B[], int next[]){
int i , j, len;
next[0] = -1;
for(i = 1; B[i] != '\0'; i++){ //依次求next[i]
for(len = i - 1; len > 0; len--){//len:相等时的前缀的最大长度为i-1,不满足相等时就依次减小 直到找到最大长度 即为next【j】
for(j = 0; j < len; j++){ //比较B的前缀和后缀是否相等
if(B[j] != B[i - len + j])
break;
}
if(j == len){//上步for循环跳出后,表示j走到了最大相等前缀的下一位,也等于前缀的长度
next[i] = len ;
break;
}
}
if(len < 1){//前缀的最大长度为0 时
next[i] = 0;
}
}
}
方法二:
void GetNext1( char B[], int next[]){
int j = 0, k = -1;
next[0] = -1;
while(B[j] != '\0'){
if(k == -1){ //无相等子串
j++;
next[j] = 0;
}else if(B[j] == B[k]){
next[++j] = k + 1;
}else{
k = next[k];
}
}
}
BM算法:
int BMSearch(char *buf, int blen, char *ptrn, int plen, int *skip, int *shift)
{
int b_idx = plen;
if (plen == 0)
return 1;
while (b_idx <= blen)//计算字符串是否匹配到了尽头
{
int p_idx = plen, skip_stride, shift_stride;
while (buf[--b_idx] == ptrn[--p_idx])//开始匹配
{
if (b_idx < 0)
return 0;
if (p_idx == 0)
{
return b_idx; //匹配到
}
}
skip_stride = skip[(unsigned char)buf[b_idx]];//根据坏字符规则计算跳跃的距离
shift_stride = shift[p_idx];//根据好后缀规则计算跳跃的距离
b_idx += (skip_stride > shift_stride) ? skip_stride : shift_stride;//取大者
}
return 0;
}
BM算法获取坏字符表:
int* MakeSkip(char *btrn, int bLen)
{
int i;
//为建立坏字符表,申请256个int的空间
//PS:之所以要申请256个,是因为一个字符是8位,
// 所以字符可能有2的8次方即256种不同情况
int *skip = (int*)malloc(256*sizeof(int)); //头指针
if(skip == NULL)
{
printf("Error");
return 0;
}
//初始化坏字符表,256个单元全部初始化为pLen
for(i = 0; i < 256; i++)
{
*(skip+i) = bLen;
}
//给表中需要赋值的单元赋值,不在模式串中出现的字符就不用再赋值了
//赋值,从左到右遍历btrn,这样如果一个字符出现两次,后面的覆盖前面的,
//不在模式中出现的字符不用再赋值,它们使用默认值bLen。
while(bLen != 0)
{
*(skip+(int)*btrn++) = bLen--;
}
return skip;
}
BM算法获取好后缀表:
int* MakeShift(char* btrn,int bLen)
{
//为好后缀表申请pLen个int的空间
//这样,第一个位置放置长度为1的后缀
int *shift = (int*)malloc(bLen*sizeof(int));
int *sptr = shift + bLen - 1;//方便给好后缀表进行赋值的指标
char *pptr = btrn + bLen - 1;//记录好后缀表边界位置的指标
char c;
if(shift == NULL)
{
printf("Error");
return 0;
}
c = *(btrn + bLen - 1);//保存模式串中最后一个字符,因为要反复用到它
*sptr = 1;//以最后一个字符为边界时,确定移动1的距离(因为要与坏字符规则比较,所以这个是个假设,1也是最小的移动距离)
pptr--;//边界移动到倒数第二个字符
while(sptr-- != shift)//该最外层循环完成给好后缀表中每一个单元进行赋值的工作
{
char *p1 = btrn + bLen - 2, *p2,*p3;
//该do...while循环完成以当前pptr所指的字符为边界时,要移动的距离
do{
while(p1 >= btrn && *p1-- != c);//该空循环,寻找与最后一个字符c匹配的字符所指向的位置
p2 = btrn + bLen - 2;
p3 = p1;
while(p3 >= btrn && *p3-- == *p2-- && p2 >= pptr);//该空循环,判断在边界内字符匹配到了什么位置
}while(p3 >= btrn && p2 >= pptr);
*sptr = shift + bLen - sptr + p2 - p3;//保存好后缀表中,以pptr所在字符为边界时,要移动的位置
pptr--;//边界继续向前移动
}
return shift;
}
主函数:
int main()
{
char A[] = "abcdesdeacd";
char B[] = "acd";
int k = BF(A, B);
printf("%d\n", k);
k = KMP(A, B);
printf("%d\n", k);
char *T = "abcdesdeacd";
char *P = "acd";
int *skip = NULL;
int *shift = NULL;
skip = MakeSkip(P, strlen(P));
shift = MakeShift(P, strlen(P));
printf("%d\n", BMSearch(T, strlen(T), P, strlen(P), skip, shift) + 1);
return 0;
}
测试结果: