数据结构——串(kmp算法)

是由零个或多个字符组成的有限序列,一般记为S=‘a1a2……an’
若两个串长度相等且每个对应位置的字符都相等时,称这两个串是相等的

子串

S=‘Hello World’ S1=‘Hello’ S2’World’ S1和S2都是S的子串

串的储存结构

1.定长顺序存储和堆存储结构

#define MAXLEN 255
//定长
typedef struct {
    char ch[MAXLEN];
    int length;
}SString;
//堆存储
typedef struct {
    char *ch;
    int length;
}HString;

2.链式存储结构
每个单链表表示一个字母

串的基本操作

StrAssign(&T,chars):赋值操作。把串t赋值为chars
StrCompare(S,T):比较操作,若S>T则返回值>0;若S=T,则返回0;若S<T则返回值<0
StrLength(S):求串长。
SubString(&Sub,S,pos,len)求子串.用Sub储存S中从POS开始长度为len的子串
concat(&T,S1,S2):串联接用T返回连接S1和S2的新串
最小操作子集

串的模式匹配

利用基本操作进行匹配

Index(S,T,pos):定位操作,若主串S中存在与串T值相同的子串,则返回它在主串中的第POS个字符后第一次出现的位置,否则返回0

int Index(SString S,SString T,int pos){
    if(pos>0){
        int n=StrLength(S);
        int m=StrLength(T);
        int i=pos;
        SString sub;
        while(i<n-m+1){
            SubString(sub,S,i,m);
            if (StrCompare(sub,T)!=0)
                i++;
            else
                return i;
        }
    }
    return 0;
}

简单的模式匹配算法

原理:

设置变量i表示主串的位置,i=1表示主串的第一个元素,设置变量j表示匹配子串的位置,j=1表示第一个元素,比较主串和配置子串的第一个元素,如果相等则,一次比较后面的元素,直到j大于匹配子串的长度则匹配成功,如果主串i对应元素与和匹配子串j对应元素不相等,则i返回j-2个位置,j重置为1,重新开始匹配,若主串一直没有匹配成功而剩余匹配元素低于匹配子串长度,则直接退出在这里插入图片描述

简单匹配代码

//简单匹配算法
int Index2(SString S,SString T,int pos){
    int i=pos,j=1;
    while (i<=S.length-T.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;
}

KMP算法

原理

字符串的前缀,后缀匹配部分

1.前缀:一个字符串,从前面开始排,所包含的所有子串。如:“abcdefg”的前缀就是a,ab,abc,abcd,abcde,abcdef
2.后缀:一个字符串,从后面开始排,所包含的所有子串。如:“abcdefg”的后缀就是g,fg,efg,defg,cdefg,bcdefg
3.匹配值为;一个字符串的前缀和后缀共同的元素的最大长度,
如,上面的例子中没有一个元素是相等的则部分匹配值为0
如:“aba”的前缀是a,ab,后缀是a,ba ,其中有共同的元素a,而a的长度是1,所以“aba”的匹配值为1
如:“ababa”的前缀是a,ab,aba,abab,后缀a,ba,aba,baba,其中有共同的元素a和aba而其中aba的长度是3最大,所以ababa的匹配值是3
4.部分匹配值(MP):字符串和他所有的前缀的匹配值,如:ababa,前缀a的匹配值为0,前缀ab的匹配值为0,前缀aba的匹配值为1,前缀abab的匹配值为2,字符串本身ababa的匹配值为3,则字符串的部分匹配值(MP)为00123
在这里插入图片描述

计算部分匹配值(next数组的计算)
//kmp计算部分匹配值
void get_next(SString T,int next[]){
    int i=1,j=0;
    next[1]=0;
    while (i<T.length){
        if (j==0||T.ch[i]==T.ch[j]){
            ++i;++j;next[i]=j;
        } else
            j=next[j];
    }
}
字符串匹配

基本原理和简单匹配算法一样,都是主串和匹配串分别设置指针,一个一个比较,最后找出子串,但是简单匹配算法有个缺点,每次比较失败之后i会退回j-2个位置,但是中间明显有一部分是匹配不成功的(因为不包含匹配算法的任何前缀),于是为了避免i回退,我们通过前缀和后缀的比较,如果比较的元素可能出现匹配值的前缀我们再去比较,如果根本不包含匹配值的前缀那就直接跳过刚刚比较过的这一部分,而如果存在相同的部分,只需要把j往后退对应的部分匹配值,这样的话i就永远不会回退。

如图:如果匹配过程中发现不匹配的元素,按照简单匹配算法则要再多匹配3次才可以匹配成功,而通过KMP算法则可以跳过中间两个浅绿色的部分,如图原来已经匹配了5个元素(黄色方框的元素),只需要经过前缀后缀的比较发现,前缀中有个ba,后缀中也有个ba,中间没有相同的前后缀,则直接跳过中间的a和c直接从第二个b开始比较,而因为前面已经比较过了bacba所以后面两个ba已经比较过了,所以字符串的j直接从第三个c开始比较就可以了,不用退回到1开始比较(退回的个数为对应部分匹配值得个数:2个)
在这里插入图片描述

int Index_KMP(SString S,SString T,int next[],int pos){
    int i=pos ,j=1;
    while (i<=S.length&&j<=T.length) {
        if (j == 0 || S.ch[i] == T.ch[j]) {
            i++;
            j++;
        } else {
            j = j - 1 - next[j - 1];
        }
    }
        if (j>T.length){
            return i-T.length;
        } else {
            return 0;
        }
}

KMP改进算法(nextval数组)

在KMP算法中通过next数组来判断子串j指针在比较失败之后的位置,但是如果比较失败的字母和next指向的字母刚好是相等的,那么按照kmp算法的逻辑,就应该比较当前主串指针i指向的字母和next指向的字母,由于之前比较失败的字母和next指向的字母相等,所以主串i和next指向的字母比较是毫无意义的(i和j已经失败了,next=j,所以i和next也必定失败)。如下图字符与第4个字符g不匹配,而第四个字符匹配失败时候j指向1,第一个字符还是g那肯定也不匹配(这次匹配毫无意义)。
在这里插入图片描述
所以如果next数组中出现字母与跳转后的字母相等的情况,就直接把这个字母的next 改为跳转之后的next值,新的这个数组被称之为nextval
在这里插入图片描述
如果出现子串中多次重复相同的字母nextval数组就比next要快的多
在这里插入图片描述
next数组变nextvl数组

for(int j=2;j<=T.length;j++){
	if(T.ch[next[j]]=T.ch[j])
		nextval[j]=nextval[next[j]];
	else
		nextval[j]=next[j];	
}

真题

2019:abaabaabcabaabc主串匹配abaabc,手动计算比较次数,比较到第6个字符的时候发现不匹配,主串不动,子串退回到前面可能匹配的前缀,从刚才匹配失败的地方重新匹配,比如abaaba匹配失败用了6次,把子串像前平移到匹配失败之前的字符全部重合位置,比如子串前进3个字符就可以和主串的aba匹配成功,这个时候继续比较主串和子串从第6个字符开始,到第10个字符匹配成功,一共匹配了4次,加上前面的6次一共10次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值