字符串-模式匹配算法

1:BF(brute-force)模式匹配

复制代码
int Index(char S[],int pos,char T[])
{
    int i=pos;//主串开始位置
    int j=1;
    while(i<=strlen(S)&&j<=strlen(T))
    {
        if(S[i]==T[j])
        {
            i++;
            j++;
        }
        else
        {
            i=i-j+2;//主串回溯到该位置重新比较
            j=1;
        }
    }
    if(j>strlen(T))
    {
        return i-strlen(T);
    }
    else
    {
        return 0;
    }
}
复制代码

2:KMP模式匹配

复制代码
解析
next函数
当j=1             0
当此集合不为空    max{k|1<k<j且'T1.....T(k-1)'='T(j-k+1)...t(j-1)}
其他              1

 当next函数定义中的集合不为空时,next[j]的值等于串’T1T2…T(j-1)'的真前缀子串和真后缀子串相等时的最大子串长度+1
  a b a a b c a c
j 1 2 3 4 5 6 7 8
j=1   0
j=2   1
j=3  a ab      0+1=1
j=4  a aba     1+1=2
j=5  b abaa    1+1=2
j=6  c abaab   2+1=3
j=7  a abaabc  0+1=1
j=8  c abaabca 1+1=2
在匹配过程中Si=Tj,则i和j分别增1;
否则,i不变,而j退到next[j]位置再比较(即Si和Tnext[j]进行比较),
若相等,则指针各自增1,
否则j再退到下一个next值得位置

此程序中,next 数组使用的下标初始值为 1 ,
next[0] 没有用到(也可以存放 next 数组的长度)。
而串的存储是从数组的下标 0 开始的,所以程序中为 T[i-1] 和 T[j-1]。
具体的算法如下:

模式串T为(下标从1开始):“abcabac”
next数组(下标从1开始): 01
第三个字符 ‘c’ :由于前一个字符 ‘b’ 的 next 值为 1 ,取 T[1] = ‘a’ 和 ‘b’ 相比较,不相等,继续;由于 next[1] = 0,结束。 
‘c’ 对应的 next 值为1;(只要循环到 next[1] = 0 ,该字符的 next 值都为 1 ) 模式串T为: “abcabac” next数组(下标从1开始):011 第四个字符 ’a‘ :由于前一个字符 ‘c’ 的 next 值为 1 ,取 T[1] = ‘a’ 和 ‘c’ 相比较,不相等,继续;由于 next[1] = 0 ,结束。
‘a’ 对应的 next 值为 1 ; 模式串T为: “abcabac” next数组(下标从1开始):0111 第五个字符 ’b’ :由于前一个字符 ‘a’ 的 next 值为 1 ,取 T[1] = ‘a’ 和 ‘a’ 相比较,相等,结束。
‘b’ 对应的 next 值为:1(前一个字符 ‘a’ 的 next 值) + 1 = 2 ; 模式串T为: “abcabac” next数组(下标从1开始):01112 第六个字符 ‘a’ :由于前一个字符 ‘b’ 的 next 值为 2,取 T[2] = ‘b’ 和 ‘b’ 相比较,相等,所以结束。
‘a’ 对应的 next 值为:2 (前一个字符 ‘b’ 的 next 值) + 1 = 3 ; 模式串T为: “abcabac” next数组(下标从1开始):011123 第七个字符 ‘c’ :由于前一个字符 ‘a’ 的 next 值为 3 ,取 T[3] = ‘c’ 和 ‘a’ 相比较,不相等,继续;由于 next[3] = 1
所以取 T[1] = ‘a’ 和 ‘a’ 比较,相等,结束。
‘a’ 对应的 next 值为:1 ( next[3] 的值) + 1 = 2 ; 模式串T为: “abcabac” next数组(下标从1开始):0111232
复制代码
复制代码
void GetNext(int *next,char *T)
{
//  a b a a b c a c
//j 1 2 3 4 5 6 7 8
//k 0 1 2 3 4 5 6 7
    int i=1,j=0;
    next[i]=0;
    while(i<strlen(T))//1<8 2<8 2<8 3<8
    {
        if(j==0||T[i-1]==T[j-1])//k==0,t[1]=t[0] 2=1 2=0 3=2
        {
            ++i;//j=2 3 4
            ++j;//k=1 2 3
            next[i]=j;//next[2]=1,next[3]=2 next[4]=3
        }
        else
        {
            j=next[j];//0
        }
    }
}
int KMP(char *s,char *t)
{
    int next[10];
    GetNext(next,t);
    int i=1,j=1;
    while(i<=strlen(s)&&j<strlen(t))
    {
        //j==0:代表模式串的第一个字符就和当前测试的字符不相等;S[i-1]==T[j-1],
        //如果对应位置字符相等,两种情况下,指向当前测试的两个指针下标i和j都向后移
        if(j==0||s[i-1]==t[j-1])
        {
            i++;
            j++;
        }
        else
        {
            j=next[j];//如果测试的两个字符不相等,i不动,j变为当前测试字符串的next值
        }
    }
    if(j>strlen(t))//如果条件为真,说明匹配成功
    {
        return i-strlen(t);
    }
    return -1;
}
int main() {
    int i=KMP("ababcabcacbab","abcac");
    printf("%d",i);
    return 0;
}
复制代码

3:升级KMP模式匹配

复制代码
解析
模式串T:a b c a c
 next :0 1 1 1 2
 在模式串“abcac”中,有两个字符 ‘a’,我们假设第一个为 a1,第二个为 a2。
 在程序匹配过程中,如果 j 指针指向 a2 时匹配失败,那么此时,主串中的 i 指针不动,j 指针指向 a1 ,很明显,
 由于 a1==a2,而 a2!=S[i],所以 a1 也肯定不等于 S[i]。

 为了避免不必要的判断,需要对 next 数组进行精简,
 对于“abcac”这个模式串来说,由于 T[4] == T[next[4]] ,
 所以,可以将next数组改为:
 模式串T:a b c a c
  next  :0 1 1 0 2
复制代码
复制代码
 void GetNext(int *next,char *T)
{
    int i=1,j=0;
    next[i]=0;
    while(i<strlen(T))
    {
        if(j==0||T[i-1]==T[j-1])
        {
            ++i;
            ++j;
            if (T[i-1]!=T[j-1]) {
               next[i]=j;
            }
            else{
                next[i]=next[j];
            }
        }
        else
        {
            j=next[j];//0
        }
    }
}
复制代码

 

posted on   Y-flower  阅读(186)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示