14. 串的模式匹配

一、什么是串

  ,即 字符串(String)是由零个或多个字符组成的有限序列,一般即为 \(S="a_{1}a_{2}...a_{n}"\)。其中,S 是 串名,双引号引起的字符序列就是 串的值\(a_{i}\) 可以是字母、数字或其它字符。串中字符的个数 n 称为 串的长度。n=0 时的串称为 空串。其中,串中任意个数 连续的 字符组成的序列称为 子串

二、串的模式匹配

  比较朴素的模式匹配算法是用两个指针 i 和 j 分别指向主串和模式串,如果两个指针指向的元素相同则指针后移,不相同则需要回退指针(主串指针回退到上次匹配首位的下一个位置,模式串指针回退到开头位置),重复进行上述操作直到主串指针或模式串指针指向末尾。如果最后是模式串指针指向末尾,则模式串与主串匹配成功,否则匹配失败。

朴素模式匹配

  使用朴素模式匹配的方式,存在大量指针回溯的问题,时间运行效率低下。此时我们可以使用 KMP 算法进行模式匹配。

  在字符匹配的过程中,在匹配失败的那个字符前,主串和模式串一定是一致的。这时,我们可以分析模式串,获取模式串最大最大相等的前缀(前缀指的是不包含尾字符的所有子串)和后缀(后缀指的是不包含首字符的所有子串)。此时,主串的指针不用移动,只回退模式串的指针,让模式串的前缀对其主串的后缀。

KMP模式匹配

  为此,我们需要求当不同位置的字符失配时的模式串的最大相等的前缀和后缀,然后将值保存到 match 数组中,即 match 数组用来表明第 j 个字符匹配时,模式串中需要重新和主串进行比较的字符的位置。

  假设已经求得 next[j-1]=t,据此可以分两种情况来推导 next[j] 的值:

  • 情况 1:若 \(P_{j} = P_{t}\),则 match[j] = t+1 = match[j-1]+1。
  • 情况 2:若 \(P_{j} ≠ P_{t}\),则循环的将 t 赋值为 match[t],直到 t=0 或者满足情况 1 位置,当 t=0 时, match[j+1] = 1。

求next数组

/**
 * @brief 构建匹配数组
 * 
 * @param pattern 模式串
 * @param match 匹配数组
 */
void BuildMatch(char * pattern, int * match)
{
    int i = 0, j = 0;
    int m = strlen(pattern);

    match[0] = -1;
    for (j = 1; j < strlen(pattern); j++)
    {
        i = match[j - 1];

        while ((i >= 0) && (pattern[i + 1] != pattern[j]))
        {
            i = match[i];
        }
    
        if (pattern[i+1] == pattern[j])
        {
            match[j] = i + 1;
        }
        else
        {
            match[j] = -1;
        }
    }
}

/**
 * @brief KMP模式匹配
 * 
 * @param str 主串
 * @param pattern 模式串
 * @param next 匹配数组
 * @return int 匹配成功,返回匹配位置的索引,匹配失败,返回-1
 */
int KMP(char * str, char * pattern)
{
    int i = 0, j = 0;

    if (strlen(str) < strlen(pattern))
    {
        return -1;
    }
  
    int match[strlen(pattern)];
    memset(match, 0, sizeof(match));
    BuildMatch(pattern, match);

    while (i < strlen(str) && j < strlen(pattern))
    {
        // 匹配成功,则主串和模式串指针同时向后移动一位
        if (str[i] == pattern[j])                                           
        {
            i++;
            j++;
        }
        // 第一个字符匹配成功,后面匹配失败,则移动模式串指针
        else if (j > 0)
        {
            j = match[j - 1] + 1;
        }
        // 如果第一个字符匹配失败,则模式串指针指向0,主串指针向后移动一位
        else
        {
            i++;
        }
    }

    return (j == strlen(pattern)) ? (i - j) : -1;
}
posted @ 2023-07-11 17:28  星光樱梦  阅读(16)  评论(0编辑  收藏  举报