9、数据结构与算法 - 字符匹配问题的 BF算法 和 RK算法

字符匹配问题

有个一主串 S = {a,b,c,a,c,a,b,d,c},模式串T={a,b,d};请找到模式串在主串中第一次出现的位置;

提示:不需要考虑字符串大小写问题,字符均为小写字母;

分析:

#include <stdio.h>
#include "string.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"
//#include "ctype.h"

#define OK 1
#define ERROR 0
#define TURE 1
#define FALSE 0

 

1、BF算法 - 暴风算法

#define MAXSIZE 40 //初始化分配空间
typedef int Status; //Status 是函数的类型,其值是函数结果状态码,如OK 等
typedef int ElemType; // ElemType 类型是根据实际情况而定,这里假设为int
typedef char String[MAXSIZE+1];//0 号单位元存放串的长度,所以申请内存的时候要在初始化空间大小下 +1

1、生成一个其值等于chars 的串T

//生成一个其值等于chars 的串T
Status StrAssign(String T, char * chars){

    if (strlen(chars)>MAXSIZE) {
        return ERROR;
    }else{
        T[0] = strlen(chars);
        for (int i=1; i<=T[0]; i++) {
            T[i] = *(chars+i-1);
        }
        return OK;
    }
}

2、清空串

//清空串
Status ClearString(String S){
    S[0] = 0;//串长度置为0即可
    return OK;
}

3、输出字符串 T

//输出字符串 T
void StrPrint(String T){
    for (int i=1; i<=T[0]; i++) {
        printf("%c ",T[i]);
    }
    printf("\n");
}

3、输出Next数组值

//输出Next数组值
void NextPrint(int next[],int length){
    for (int i=1; i<=length; i++) {
        printf("%d ",next[i]);
    }
    printf("\n");
}

4、返回串的元素个数

用串的第一位存放串的长度

//返回串的元素个数
int StrLength(String S){
    return S[0];
}

字符串匹配问题 

思路:
1、分别l利用指针i 和j 指示主串S 和模式T 中当前等待比较的字符位置,i初值为pos ,j初值为1;
2、如果2个串均比较到了串尾,即i 和 j均小于等于S 和T的长度时,则循环执行以下的操作
    (1)、S[i] 和 T[j]比较,若相等,则i 和 j分别指示串中下一个位置,继续比较后续的字符;
    (2)、若不相等,指针后退重新开始匹配。从主串的下一个字符串(i= i-j+2)起再重新和模式第一个字符(j = 1)比较;
3、如果 j > T.length,说明模式T 中的每个字符串依次和主串S 栈中的一个连续字符序列相等,则匹配成功,返回和模式T 中第一个字符一样的字符在主串中S 中的序号(i-T.length);否则匹配失败,返回0;

int Index_BF(String S, String T, int pos){
//    i用于主串S 中当前位置下标值,若pos 不为1,则从pos 位置开始匹配
    int i = pos;
//    j 用于子串T 中当前位置下标值
    int j = 1;
    
//    若i 小于S 的长度并且j 小于T 的长度时,循环继续
    while (i<= S[0] && j<= T[0]) {
        
//        比较的二个字母相等,则继续比较
        if (S[i] == T[j]) {
            i++;
            j++;
        }else{
//            不相等,则指针后退重新匹配
//            i 退回到上次匹配的首位之的下一位
//            加1,是因为子串的首位是从1开始的
//            再加1,是因为从上次匹配的首位的下一位开始
            i = i-j+2;
//            j退到子串T的首位
            j = 1;
        }
    }
    
//    如果j>T[0],则找到了匹配模式(因为子串遍历完了都是相等的)
    if (j>T[0]) {
//        i母串遍历的位置 - 模式字符串长度 = index 位置
        return i-T[0];
    }else{
//        主串中没有匹配的字符串
        return -1;
    }
}
int main(int argc, const char * argv[]) {
    //************ BF算法  ***************
    int i,*p;
    String s1,s2;
    
    StrAssign(s1, "abcdex");
    printf("s1子串为");
    StrPrint(s1);
    
    
    StrAssign(s2, "ex");
    printf("s2子串为");
    StrPrint(s2);
    
    i = Index_BF(s1, s2, 1);
    printf("i = %d\n",i);
    
    return 0;
}

//*********************************
s1子串为a b c d e x 
s2子串为e x 
i = 5


2.RK算法

 

//RK算法

//表示进制
#define d 26

1、二次确认,避免哈希冲突 造成错误

//1、为了杜绝哈希冲突,当发现模式串和子串的HashValue 是一样的时候,还是需要二次确认2 个字符串是否相等。
//S 主串  i 相同的字符串首位下标  P 子串  m 子串长度
int isMatch(char *S, int i, char *P, int m){
    int is,ip;
//    主串从开始,子串从0开始。均为未遍历到子串对应长度的的时候,如果出现不相同就不匹配。否者匹配
    for (is =i,ip=0; is != m+i && ip != m; is++, ip++) {
        if (S[is] != P[ip]) {
            return 0;
        }
    }
    return 1;
}

2、算出 d进制下的最高位 

//2、算出 d进制下的最高位
//d^(m-1)位的值
int getMaxValue(int m){
    int h =1;
    for (int i =0; i< m-1; i++) {
        h = (h * d);
    }
    return h;
}

字符串匹配的 RK算法

//若成功匹配返回主串中的偏移,否则返回-1
int RK(char *S, char *P){
    //1、n:主串长度  m:子串长度
    int n = (int)strlen(S);
    int m = (int)strlen(P);
    printf("主串长度为:%d \n子串长度为:%d\n",n,m);
    
//    A模式串的哈希值;St.主串分解子串的哈希值;
//    无符号整形
    unsigned int A = 0;
    unsigned int St = 0;
    
//    2、求得子串中 0 -m字符串的哈希值[计算子串与主串0-m]的哈希值
//    循环[0,m]获取模式串A 的HashValue 以及主串第一个[0,m] 的hasValue
    //例如:
    //此时主串:"abcaadddabceeffccdd" 它的[0,2]是ab
    //此时模式串:"cc"
    //cc = 2 * 26^1 + 2 *26 ^0 = 52+2 = 54;
    //ab = 0 * 26^1 + 1 *26^0 = 0+1 = 1;
    
    for (int i=0; i !=m; i++) {
        //第一次 A= 0*26+2;
        //第二次 A= 2*26+2;
        A = (d*A + (P[i] - 'a'));
        
        //第一次 st = 0*26+0
        //第二次 st = 0*26+1
        St = (d*St + (S[i]-'a'));
    }
    
//    3、获取d^(m-1) 值(因为后面多处用到,避免重复计算)
    int hValue = getMaxValue(m);
    
//    4、遍历[0,n-m],判断模式串HashValue A是否和其他子串的HashValue 一致。
//    不一致则继续求得下一个HashValue
//    如果一直则进入二次确认判断,2个字符串是否真正相等。否者哈希值错误继续判断
//    注意细节
//    (1)、在进入循环时,就已经得到了子串的哈希值以及主串的[0,m] 的哈希值,可以直接进入第一轮比较;
//    (2)、哈希值相等后,利用在循环之前已经计算好的st[0] 来计算后面的st[1];
//    (3)、如果不相等,利用在循环之前已经计算好的st[0] 来计算后面的st[1];
//    (4)、在对比过程,并不是一次性把所有的主串都求解好hash值。而是借助s[i] 来求解 s[i+1]。 简单说就是一边比较哈希值,一边计算哈希值;
    
    for (int i=0; i<=n; i++) {
        if (A == St) {
            if (isMatch(S, i, P, m)) {
//                加1 因为从1开始
                return i+1;
            }
        }
//        取出原来的第一位,加上新添加的最后一位。
//        例:
//        主串:abc 子串:cc
//        原:0*26^1 + 1 = St
//        计算:St =(St - 0*26^1)*26 + 2
        St = ((St - hValue*(S[i]-'a'))*d + (S[i+m] - 'a'));
    }
    return -1;
}
int main(int argc, const char * argv[]) {
    //************ RK算法  ***************
    char *buf="abcababcabx";
    char *ptrn="cab";
    printf("主串为%s\n",buf);
    printf("子串为%s\n",ptrn);
    
    int index = RK(buf, ptrn);
    printf("find index : %d\n",index);
    
    return 0;
}

//***********************************
主串为abcababcabx
子串为cab
主串长度为:11 
子串长度为:3
find index : 3
发布了104 篇原创文章 · 获赞 13 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/shengdaVolleyball/article/details/105683179