📜  在 C++ 中找到模式(1)

📅  最后修改于: 2023-12-03 14:50:52.362000             🧑  作者: Mango

在 C++ 中找到模式

模式匹配(Pattern Matching)是计算机科学中的一个常见问题,它涉及到在一个文本串中查找一个模式串的出现情况。

在 C++ 中,可以使用多种不同的算法实现模式匹配,本文将介绍其中最常见的几种算法。

算法一:暴力匹配

暴力匹配(也称为朴素匹配)是一种简单但低效的算法,它从文本串的第一个字符开始,依次比较文本串和模式串的每一个字符,直到找到一个完美匹配或者遍历完整个文本串。

其代码实现如下:

int patternSearch(const char* text, const char* pattern) {
    int n = strlen(text);
    int m = strlen(pattern);

    for (int i = 0; i <= n - m; ++i) {
        int j;
        for (j = 0; j < m; ++j) {
            if (text[i+j] != pattern[j]) {
                break;
            }
        }
        if (j == m) {
            return i; // 完美匹配,返回匹配位置
        }
    }

    return -1; // 没有找到匹配
}

该算法的时间复杂度为 $O(nm)$,其中 $n$ 是文本串的长度,$m$ 是模式串的长度。在最坏情况下,即文本串和模式串没有任何匹配时,该算法需要比较 $n \cdot m$ 次字符,耗费较长时间。但是,在一些情况下(比如文本串和模式串长度较小),暴力匹配依然是一种实用的算法。

算法二:KMP 算法

KMP 算法(也称为 Knuth-Morris-Pratt 算法)是一种高效的算法,它可以在 $O(n+m)$ 的时间复杂度内解决模式匹配问题。该算法的核心思想是利用已经匹配的前缀子串的信息,避免进行不必要的比较。

其代码实现如下:

int patternSearch(const char* text, const char* pattern) {
    int n = strlen(text);
    int m = strlen(pattern);

    int* next = (int*) malloc(sizeof(int) * m);

    int i = 0, j = -1;
    next[0] = -1;

    while (i < m) {
        if (j == -1 || pattern[i] == pattern[j]) {
            ++i;
            ++j;
            next[i] = j;
        } else {
            j = next[j];
        }
    }

    i = 0;
    j = 0;

    while (i < n && j < m) {
        if (j == -1 || text[i] == pattern[j]) {
            ++i;
            ++j;
        } else {
            j = next[j];
        }
    }

    free(next);

    if (j == m) {
        return i - m; // 完美匹配,返回匹配位置
    } else {
        return -1; // 没有找到匹配
    }
}

KMP 算法首先通过预处理模式串,计算出一个 next 数组。该数组存储了模式串中每一个位置前面的最长可匹配前缀子串的长度。一旦文本串中某个字符和模式串中当前字符不匹配,该算法就可以利用 next 数组中的信息,将模式串的匹配位置向后移动若干个字符,而不用回到前面已经匹配的位置重新比较。

算法三:Boyer-Moore 算法

Boyer-Moore 算法是一种经典的字符串匹配算法,它采用了多种启发式策略,在许多实际应用中比暴力匹配和 KMP 算法效率更高。

其代码实现如下:

int patternSearch(const char* text, const char* pattern) {
    int n = strlen(text);
    int m = strlen(pattern);

    int badChar[256] = {0};

    for (int i = 0; i < m; ++i) {
        badChar[pattern[i]] = i + 1;
    }

    int i = 0;
    while (i <= n - m) {
        int j = m - 1;
        while (text[i+j] == pattern[j]) {
            if (j == 0) {
                return i; // 完美匹配,返回匹配位置
            }
            --j;
        }
        i += std::max(1, j - badChar[text[i+j]]);
    }

    return -1; // 没有找到匹配
}

Boyer-Moore 算法分别从匹配串的尾部开始和文本串的尾部进行匹配,利用了两种启发式策略:坏字符规则和好后缀规则。

其中,坏字符规则(Bad Character Rule)在匹配过程中发现两个字符不匹配时,根据坏字符在匹配串中对应位置移动匹配串。好后缀规则(Good Suffix Rule)在发现某一段匹配字符时,根据好后缀的对应位置移动匹配串。

需要注意的是,该算法在最坏情况下的时间复杂度依然是 $O(nm)$,但是,在实际应用中,Boyer-Moore 算法的效率通常比其他算法更高。

算法四:正则表达式匹配

正则表达式(Regular Expression)是一种描述字符串模式的语言,它可以用来匹配和替换文本中符合特定模式的字符串。在 C++ 中,可以使用一些第三方库(比如 PCRE)实现正则表达式匹配。

其代码实现如下:

#include <iostream>
#include <regex>

int main() {
    std::string text = "The quick brown fox jumps over the lazy dog.";
    std::regex pattern("quick.*fox");

    if (std::regex_search(text, pattern)) {
        std::cout << "pattern found." << std::endl;
    } else {
        std::cout << "pattern not found." << std::endl;
    }

    return 0;
}

该程序中使用了 C++ 标准库中的 std::regex 类,它提供了一组完整的正则表达式匹配操作。需要注意的是,正则表达式本身可能非常复杂,其编写需要一定的技巧和经验。

总结

本文介绍了 C++ 中几种常见的模式匹配算法,包括暴力匹配、KMP 算法、Boyer-Moore 算法和正则表达式匹配。每一种算法都有其独特的优点和限制,程序员在实际应用中应该根据具体情况选择最合适的算法。