【数据结构】串

定长顺序存储

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

堆分配存储

typedef struct {
    char *ch;//按串长分配存储区,ch指向串的基地址
    int length;//串的长度
} HString;

暴力匹配算法

int Index(SString S, SString T) {
    int i = 0, j = 0;//i为主串指针,j为模式串指针
    while (i <= S.length && j <= S.length) {
        if (S.ch[i] == T.ch[j]) {//若字符相同,继续比较下一个字符
            i++;
            j++;
        } else {//指针后退重新开始匹配
            i = i - j + 1;
            j = 0;
        }
    }
    if (j > T.length) {
        return i - T.length;
    } else {
        return 0;
    }
}

KMP

部分匹配值计算

  • 前缀:除最后一个字符以外,字符串的所有头部子串。
  • 后缀:除第一个字符以外,字符串的所有尾部子串。
  • 部分匹配值:字符串的前缀和后缀的最长相等前后缀长度。

'a’最长相等前后缀长度为0
'ab’最长相等前后缀长度为0
'aba’最长相等前后缀长度为1
'abab’最长相等前后缀长度为2
'ababa’最长相等前后缀长度为3
字符串’ababa’的部分匹配值为00123

子串’abcac’的部分匹配值为00010
写成数组形式,得到部分匹配值(PM)的表:

编号12345
Sabcac
PM00010

子串需要移动位数公式:

移动位数 = 已匹配的字符数 - 对应的部分匹配值

对算法的改进方法:

已知:右移位数 = 已匹配的字符数 - 对应的部分匹配值
写成:Move = (j-1) - PM[j-1]

next数组

使用部分匹配值时,每当匹配失败,就去找它前一个元素的部分匹配值,这样使用起来有些不方便,所以将PM表右移一位,这样哪个元素匹配失败,直接看自己的部分匹配值即可。
字符’abcac’的PM表右移一位,得到next数组:

编号12345
Sabcac
next-10001

注意

  1. 第一个元素右移以后空缺的用-1填充,因为若是第一个元素匹配失败,则需要将子串向右移动一位,而不需要计算子串移动的位数。
  2. 最后一个元素在右移的过程中溢出,因为原来的子串中,最后一个元素的部分匹配值是其下一个元素使用的,但显然已没有下一个元素,故可以舍去。

上式就改写为

Move = (j-1) - next[j]

相当于将子串的比较指针j回退到

j = j - Move = j - ((j-1)-next[j]) = next[j] + 1

为了使公式简洁、计算简单,将next数组整体+1。子串next数组也可写成:

编号12345
Sabcac
next01112

最终得到指针变化公式:

j= next[j]

next[j]的含义:在子串的第j个字符与主串发生失配时,则调到子串的next[j]位置重新与主串当前位置进行比较。

  • 根据《最大长度表》,失配时,模式串向右移动的位数 = 已经匹配的字符数 - 失配字符的上一位字符的最大长度值
  • 而根据《next 数组》,失配时,模式串向右移动的位数 = 失配字符的位置 - 失配字符对应的next 值
  • 其中,从0开始计数时,失配字符的位置 = 已经匹配的字符数(失配字符不计数),而失配字符对应的next 值 = 失配字符的上一位字符的最大长度值,两相比较,结果必然完全一致。

求next值

/**
 * 求next值
 */
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;//若pi=pj,则next[j+1]=next[j]+1
        } else {
            next[j] = i;//否则j=next[j],循环继续
        }
    }
}

KMP算法

int Index_KMP(SString S, SString T, int next[]) {
    int i = 1, j = 1;
    while (i < S.length && j < T.length) {
        if (j = 0 || S.ch[i] == T.ch[j]) {//继续比较后继字符
            ++i;
            ++j;
        } else{
            j=next[j];//模式串向后移动
        }
        if (j>T.length){
            return i-T.length;//匹配成功
        } else{
            return 0;
        }

    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值