数据结构——串的模式匹配算法

1.朴素的模式匹配算法

      Index(S,T,pos):将主串S的第pos个字符和模式串的第一个字符比较(pos是位置,在算法中要转化为索引),若相等,继续逐个比较后续字符。如不等,从主串的下一个字符起,重新与模式串的第一个字符比较。直到匹配成功或全部遍历完成为止。

       这里需要说明一个前提:在一些教科书中,序号默认是从1开始的(序号为0的地方储存串的长度),但是在C/C++语言中,字符串都是从序号为0的地方开始的,因此对代码做出了一些更改。

  要匹配的串称为模式串,与之比对的称为主串。

int Index(string s, string t, int pos) {
    int i = pos-1, j = 0;
    while (i < s.length() && j < t.length()) {
        if (s[i] == t[j])
        {
            ++i;
            ++j;
        }
        else
        {
            i = i - j + 1;
            j = 0;
        }
    }
    if (j >= t.length())
        return i - t.length()+1;//返回的是位置,索引=位置-1
    else
        return 0;
}

(1)初始化

int i = pos-1, j = 0;

pos是主串开始检索的位置对应i的索引为pos-1。要匹配的串则是从序号为0的地方开始。

(2)核心代码

    while (i < s.length() && j < t.length()) {
        if (s[i] == t[j])
        {
            ++i;
            ++j;
        }
        else
        {
            i = i - j + 1;
            j = 0;
        }
    }

  while退出的条件主串遍历完毕或者配对成功。i<s.length()是主串还没有遍历完毕的时候,当i>=s.length()的时候,就说明主串已经遍历完毕了。而如果j>=t.length()的话,说明配对成功。

  如果s[i]==t[j],说明对应部分比对上了,那么i和j都需要移动一个位置。

  如果s[i]!=t[j],说明比对失败,j要回到0(要匹配的串从头开始比对),而i也要移动到起始位置,只不过起始位置需要变化(上一次的起始位置+1),也就是 i=i-j+1。

(3)返回值

    if (j >= t.length())
        return i - t.length()+1;//返回的是位置,索引=位置-1
    else
        return 0;

     如果j>=t.length(),说明匹配成功,这样需要返回主串对应的序号(也就是从哪里开始比对上的),i-t.length()是起始的索引,位置=索引+1。在这里返回位置(因为索引也有可能是0)。

     如果while循环结束,j还是小于t.length(),则while循环是因为主串循环完毕退出的,说明没有匹配上,这时返回0表示匹配失败

示例:

int main() {
    string s,t;
    cout << "输入主串:";
    cin >> s;
    cout << "输入要匹配的串:";
    cin >> t;
    int h = Index(s, t, 1);
    cout << h;
    return 0;
}

2.KMP模式匹配算法 

  https://www.cnblogs.com/dusf/p/kmp.html

  这个是大神的详细解释,在这里就不重复了,只贴上代码。

(1)求next数组

void Getnext(string t, int next[])
{
    int j = 0, k = -1;
    next[0] = -1;
    while (j < t.length() - 1)
    {
        if (k == -1||t[j]==t[k])
        {
            j++; 
            k++;
            next[j] = k;
        }
        else 
            k = next[k];
    }
}

(2)KMP算法

int KMP(string s, string t)
{
    int next[MaxSize], i = 0,j = 0;
    Getnext(t, next);
    while (i < (int)s.length() && j < (int)t.length())
    { 
        if (j == -1 || s[i] == t[j])
        {
            i++;
            j++;
        }
        else
        {
            j = next[j];
        }
    }
    if (j >= t.length())
        return i - t.length();         //匹配成功,返回子串的索引
    else
        return -1;                  //没找到
}

  这里需要解释一个问题:为什么s.length()和t.length()需要强制转换为int类型?

while (i < (int)s.length() && j < (int)t.length())

     因为s.length()和t.length()返回的字符串长度是unsigned int类型,如果j==-1的话,负数与unsigned int类型比较会转化为unsigned int类型——最终结果为4294967295。

因此如果不强制转化为int类型,当j==-1的时候while循环就退出了。 

注:这里的返回值是模式串在主串里的索引,如果想返回位置则需要+1。

(3)KMP算法的改进

  其实也就是对Getnext函数的改进。

void Getnext(string t, int* next)
{
    int j = 0, k = -1;
    next[0] = -1;
    while (j < (int)t.length() - 1)
    {
        if (k == -1 || t[j] == t[k])
        {
            j++; k++;
            if (t[j] == t[k])//当两个字符相同时,就跳过
                next[j] = next[k];
            else
                next[j] = k;
        }
        else k = next[k];
    }
}

示例:

int main() {
    string s;
    string t;
    cout << "输入主串:";
    cin >> s;
    cout << "输入模式串:";
    cin >> t;
    int g= KMP(s, t);
    printf("%d", KMP(s, t));
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值