模式匹配算法

串是一种特殊结构的线性表。区别在于线性表中用ElemType存储元素,串中用char存储字符。

串的存储结构

顺序存储

//1、定长的顺序串
#define MAXLEN 255	//预定义最大串长为255
typedef struct{
    char ch[];	//每个分量存储一个字符
    int length;	//串的实际长度
}SString;

//用静态数组实现固定长度的顺序存储串
//分配连续的存储空间,每个char字符占1B,另外需要4个字节的length
//2、动态数组实现的顺序串——区别于链式存储
typedef struct{
    char* ch;	//ch指向串的基地址
    int length;	//串的实际长度
}HString;

HString S;
S.ch = (char*)malloc(MAXLEN * sizeof(char));
S.length = 0;

//动态数组实现的长度可变的串
//在堆区分配存储,用完需要手动free

串的链式存储

typedef struct StringNode{
    char ch;
    struct StringNode* next;
}StringNode,* String;

//存储密度低,每个字符1B,每个指针4B
//改进如下:
typedef struct StringNode{
    char ch[4];
    struct StringNode* next;
}StringNode,* String;

字符串模式匹配算法

字符串模式匹配:在主串中找到与模式串相同的字串,并返回其所在位置。

408中需要掌握的两种模式匹配算法:朴素模式匹配算法、KMP算法。

朴素模式匹配算法

设主串长度为n,模式串长度为m。将主串中所有长度为m的子串依次与模式串对比,直到找到一个完全匹配的子串,或所有的子串都不匹配为止。最多对比n-m+1个子串(比如主串15,模式串12,那么主串比子串长了2,在加上12自己本身,就是2+1=3个,因此最后还要再加1)。

// Index(S,T):定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0。采用的就是模式匹配算法
int Index(SString S,SString T){
    int i = 1;
    int n = StrLength(S);
    int m = StrLength(T);
    SString sub;		//用于暂存子串
    while(i<=n-m+1){	//最多对比n-m+1个子串
        SubString(sub,S,i.m);	//取出从位置i开始,长度为m的子串
        if(StrCompare(sub,T)!=0) ++i;	//如果不匹配,则匹配下一个子串
        else return i;	//返回子串在主串中的位置
    }
    return 0;		//S中不存在与T相等的子串
}
//该算法已经实现了模式串匹配算法,但是使用了SubString和StrCompare两个操作
//接下来不使用字符串的基本操作,直接通过数组下标实现朴素匹配模式算法

指针 i 指向主串 S ,指针 j 指向模式串 T ,若当前子串匹配失败,则主串指针 i 指向下一个子串的第一个位置,模式串指针 j 回到模式串的第一个位置

i = i - j + 2;	//主串指针 i 指向下一个子串的第一个位置
j = 1;		//模式串指针 j 回到模式串的第一个位置

若 j > T.length,则当前子串匹配成功,返回当前子串的第一个字符的位置 —— i - T.length

//朴素模式匹配算法
int Index(SString S,SString T){
    int i = 1, j = 1;
    while(i<= S.length && j<T.length){
        if(S.ch[i] == T.ch[j]){
            ++i;++j;
        }
        else{
            i = i-j+2;
            j=1;
        }
    }
    if(j>T.length)
        return i - T.length;
    else
        return 0;
}
//设设主串长度为n,模式串长度为m,最坏时间复杂度为O(mn)

朴素模式匹配算法思想总结:

1、主串长度为n,模式串长度为m

2、将主串中所有长度为m的子串依次与模式串对比

3、找到第一个与模式串匹配的子串,并返回子串起始位置

4、若所有子串都不匹配,则返回0

KMP算法

1、根据模式串 T,求出 next 数组

2、利用next数组进行匹配(主串指针不回溯)

手算求模式串的next数组

next数组的作用:当模式串的第 j 个字符匹配失败时,从模式串的第 next[j] 的继续往后匹配。

1、任何模式串都一样,第一个字符不匹配时,只能匹配下一个子串,因此,往后余生,next[1]都无脑写0。

2、任何模式串都一样,第二个字符不匹配时,应尝试匹配模式串的第一个字符,因此,往后余生,next[2]都无脑写1。

3、在后面的字符发生不匹配时,在不匹配的位置前面划一条分界线,模式串一步一步往后退,直到分界线之前“能对上”,或模式串完全跨过分界线为止。

获得next数组之后,就可以使用next数组进行模式匹配了。

KMP算法的进一步优化

优化之后的KMP算法逻辑不变。只是对next数组的优化,将next数组优化为了nextval数组。

先求next数组,再由next数组求nextval数组。

求nextval数组的步骤如下:

nextval[1] = 0;		//nextval[1]无脑写0
for(int j = 2; j <=T.length; j++){	//从第二个字符开始往后
    if(T.ch[next[j]] == T.ch[j])
        //如果第j个字符和根据next数组要跳转至的字符相同,则让nextval中j的值与next[j]的值相同
        nextval[j] = nextval[next[j]];
    else
        //如果不相同的话,那么nextval的j和next的j相同
        nextval[j] = next[j];
}
// T为模式串,j为模式串字符的序号

附上一张练习:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

封心心心心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值