朴素模式匹配算法

  • 特点:匹配失败时,子串回到首位,主串回到首位加一
  • 过程:从主串S的第pos个字符与子串的第一个字符开始比较,若比较成功,两串的指针加一,继续比较下一个;
    若出现比较失败,子串指针回到第一个字符,主串指针回到与子串第一个字符对应字符的下一个字符,重新开始比较。
  • 劣势:在二进制串中,若0或1多次重复出现,会出现多次回溯
  • 函数表示:
int Index(HString S,HString T,int pos){
    int i=pos,j=1;
    if(T.length>S.length-pos){
        return -1;
    }//子串长度超过主串pos后的部分,一定不成立
    while(i<S.length&&j<T.length){
        if(S.ch[i-1]==T.ch[j-1]){
            i++;
            j++;
        }
        else{
            i=i-j+1;
            j=1;
        }
        if(j=T.length){
            return i-T.length;
        }
        else{
            return -1;
        }
    }
}

KMP算法

  • 引入next数组,数组下标表示子字符串的下标,数组下标对应数字表示最长相同前缀的长度
i 0 1 2 3 4 5 6 7
子串 A B C D A B F D
next[i] -1 0 0 0 1 2 0 0

当i=0时,对于子串的首字符,不可能存在前缀,因此定义next【i】=-1

  • next数组的函数表示
void GetNext(HString S,int next[]){
    int i=0,j=-1;
    next[0]=-1;
    while(i<S.length){
        if(j==-1||S.ch[i]==S.ch[j]){
            i++;
            j++;
            next[i]=j;
        }//注释1
        else{
            j=next[j];
        }//注释二
    }
}

以下通过画数轴更易理解
注释一:由next[i] = j得,也就是对于位置 i 来说,区段 [0, i - 1] 的最长相同前缀分别是 [0, j - 1] 和 [i - j, i - 1],即这两区段内容相同
注释二:next[j]代表 [0, j - 1] 区段中最长相同前缀的长度。next【j】表示j-1段中有两小段相同,而这两小段与i-1段中后边有两段也相同,即第一段与最后一段(到i-1)相同。并且第一段的最后一个就是next【j】。这样缩短了寻找前后缀的时间。

image(此图为网图)

  • KMP算法
int KMP_index(HString S,HString T){
    int next[T.length];
    GetNext(T,next);
    int i=0,j=0;
    if(S.length<T.length){
        return -1;
    }
    while(i<S.length&&j<T.length){
        if(j==-1||S.ch[i]==T.ch[j]){
            i++;
            j++;
        }
        else{
            j=next[j];
        }
    }
    if(j==T.length){
        return i-j;
    }
    return -1;
}
  • 特点:主串指针不变,子串指针根据next数组进行改变
  • 过程:主串和子串均从第一个开始比较,若比较成功,两串指针加一,比较下一个;
    若比较失败,主串指针不变,子串指针指向next数组对应的数字(next=0,则指针指向子串第一个;next=p,则指针指向子串第p+1个)
  • 例子:如果子串为 abcdef,主串为abcdex,当第一轮匹配到第六个字符f和x的时候,匹配失败,由于子串f元素前的元素中没有相同的元素,并且与主串匹配,所以a与主串中的2-5号元素 即 bcde 都是不可能相匹配的,所有这几步都可以省略,直接让a和主串中的x去匹配
    如果子串为abcabx,主串为abcaba,在第一轮中,前五个元素子主串分别相匹配,第六个元素位置出错,由于子串中出错的位置x前的串 abcab 的前缀和后缀都是 ab,既然第一轮的时候,已经匹配成功,那就意味着,子串中的 第一第二个元素ab一定与 主串中 第四第五个元素 ab相等,所以直接可以拿子串前缀ab后面的c开始于a进行比对

无欲则刚
76 声望15 粉丝