字符串匹配经典算法



/**************************************************************************
* 本文件主要包含了字符串匹配的一些算法,这些算法可以根据实际情况在不同的
* 场景中应用。
*
* 字符串匹配问题就是在文本串T中找到模式串P出现的所有位置,就朴素算法而言,
* 在文本串和模式串的长度比较短时使用,它的时间复杂度较高,但实现以及使用比
* 较简单,而且不需要额外存储空间。
*
* 当文本串比较长时,可以使用Knuth-Morris-Pratt算法,它优化了比较失败后回退
* 的问题,从而使得时间复杂度可以下降到O(n)的级别,但是它需要额外的存储空间
* 和预处理过程。
*
* Boyer-Moore算法很类似KMP算法,它与KMP的不同体现在模式串比较顺序和遇到不
* 匹配字符右移的距离所计算的方法不同,在一般情况下,它的效率要比KMP算法高
* 几倍,但是在文本串和模式串具有周期性的情况下却会退化到O(n^2),例如,当文
* 本为ababababa,模式串为ababa时。此外,它需要的更多的存储空间。对原始的
* BM算法进行优化,可以使它在最坏情况下的时间复杂度也为O(n)。
*
* Sunday算法也是类似于KMP、BM的一种算法,它在一般情况下的效率要比KMP和BM
* 好,但是在极端情况下同BM算法一样会退化到朴素算法的级别,这跟该算法的具体
* 实现有关。对原始Sunday算法进行优化,也可以使它在最坏情况下的时间复杂度降
* 为O(n)。
*
* 以上的这些字符串匹配算法都只能解决一个模式串的问题,而Rabin-Karp算法可以
* 解决多于一个模式串的问题,但是它的使用也是有很大的局限性的,在模式串长度
* 很大的情况下不适宜使用,HASH算法也是影响执行效率的关键因素。
*
* Aho-Corasick算法是基于有限自动机的多模式串匹配算法,这种算法是基于Trie
* 的,它可以解决在一段文本中查找指定的多个模式出现的位置,该算法了构造一
* 个有限自动机,从而对文本串只需要进行一次遍历即可。AC算法的时间复杂度与构
* 造有限自动机的时间和遍历文本串的时间相关。
**************************************************************************/
#ifndef STRING_H_
#define STRING_H_

/**************************************************************************
* 朴素算法查找字符串,时间复杂度是O(nm),不需要预处理
**************************************************************************/
int naive_search(const char * txt, const char * pat, int offset[])
{
    int i, j, n, m, c = 0;
    n = strlen(txt);
    m = strlen(pat);
    for (i = 0; i <= n - m; i++) {
        for (j = 0; j < m; j++)
            if (pat[j] != txt[i + j])
                break;
        if (j == m)
            offset[c++] = i;
    }
    return c;
}

/**************************************************************************
* Knuth-Morris-Pratt算法查找字符串,预处理时间是O(m),时间复杂度是O(n)
**************************************************************************/
int kmp_search(const char * txt, const char * pat, int offset[])
{
    int i, j, n, m, c = 0;
    n = strlen(txt);
    m = strlen(pat);
    int * next = new int[m];
    for (i = 1, j = 0, next[0] = 0; i < m; i++){
        j = next[i - 1];
        while (j > 0 && pat[j] != pat[i])
            j = next[j - 1];
        if (pat[j] == pat[i])
            j++;
        next[i] = j;
    }
    for(i = 0, j = 0; i < n; i++) {
        while (j > 0 && txt[i] != pat[j])
            j = next[j - 1];
        if(txt[i] == pat[j])
            j++;
        if (j == m)
            offset[c++] = i - j + 1;
    }
    delete [] next;
    return c;
}

/**************************************************************************
* Boyer-Moore算法查找字符串,预处理时间是Θ(m + |Σ|),时间复杂度是Ω(n/m),
* O(mn),它在一般情况下比KMP要快。
**************************************************************************/
int bm_search(const char * txt, const char * pat, int offset[])
{
    int i, j, n, m, a, b, c = 0;
    n = strlen(txt);
    m = strlen(pat);
    int * bc = new int[256];
    for(i = 0; i < 256; i++)
        bc[i] = m;
    for (i = 0; i <= m - 1; i++)
        bc[(unsigned char)pat[i]] = m - i - 1;
    int * sfx = new int[m];
    sfx[m - 1] = m;
    for (i = m - 2; i >= 0; i--) {
        j = i;
        while(pat[j] == pat[m - i - 1 + j] && --j >= 0);
        sfx[i] = i - j;
    }
    int * gc = new int[m];
    for (i = 0; i < m; i++)
        gc[i] = m;
    for (i = m - 1; i >= 0; i--)
        if (sfx[i] == i + 1)
            for (j = 0; j < m - i - 1; ++j)
                if (gc[j] == m)
                    gc[j] = m - i - 1;
    for (i = 0; i <= m - 2; ++i)
        gc[m - 1 - sfx[i]] = m - i - 1;
    for (i = 0; i <= n - m; i += a > b ? a : b) {   
        j = m - 1;
        while(j >= 0 && pat[j] == txt[j + i]) j--;
        if (j < 0) {
            offset[c++] = i;
            a = b = 1;
        } else {
            a = gc[j];
            b = bc[(unsigned char)txt[j + i]] - m + j + 1;
        }
    }
    delete [] sfx; delete [] gc; delete [] bc;
    return c;
}

/**************************************************************************
* Boyer-Moore-Galil 算法查找字符串,预处理时间是Θ(m + |Σ|),时间复杂度是
* Ω(n/m), O(n),它在一般情况下比KMP要快。
**************************************************************************/
int bmg_search(const char * txt, const char * pat, int offset[])
{
    int i, j, n, m, o, a, b, c = 0;
    n = strlen(txt);
    m = strlen(pat);
    int * bc = new int[256];
    for(i = 0; i < 256; i++)
        bc[i] = m;
    for (i = 0; i <= m - 1; i++)
        bc[(unsigned char)pat[i]] = m - i - 1;
    int * sfx = new int[m];
    sfx[m - 1] = m;
    for (i = m - 2; i >= 0; i--) {
        j = i;
        while(pat[j] == pat[m - i - 1 + j] && --j >= 0);
        sfx[i] = i - j;
    }
    int * gc = new int[m];
    for (i = 0; i < m; i++)
        gc[i] = m;
    for (i = m - 1; i >= 0; i--)
        if (sfx[i] == i + 1)
            for (j = 0; j < m - i - 1; ++j)
                if (gc[j] == m)
                    gc[j] = m - i - 1;
    for (i = 0; i <= m - 2; ++i)
        gc[m - 1 - sfx[i]] = m - i - 1;
    for (i = 0, o = gc[0]; o > 0 && i <= m - 2; ++i)
        if (gc[i] != (i / o + 1) * o) o = 0;
    for (i = 0, j = m - 1; i <= n - m; i += a > b ? a : b) {
        while(j >= 0 && pat[j] == txt[j + i]) j--;
        if (j < 0) {
            offset[c++] = i;
            a = b = o ? o : 1;
            j = o ? o : m - 1;
        } else {
            a = gc[j];
            b = bc[(unsigned char)txt[j + i]] - m + j + 1;
            j = m - 1;
        }
    }
    delete [] sfx; delete [] gc; delete [] bc;
    return c;
}

/**************************************************************************
* Sunday算法查找字符串,预处理时间是Θ(m + |Σ|),时间复杂度是Ω(n/m),
* O(mn),它在一般情况下比KMP、BM要快.
**************************************************************************/
int sunday_search(const char * txt, const char * pat, int offset[])
{
    int i, j, n, m, c = 0;
    n = strlen(txt);
    m = strlen(pat);
    int * next = new int[256];
    for (i = 0; i < 256; i++)
        next[i] = m + 1;
    for (i = 0; i < m; i++)
        next[(unsigned char)pat[i]] = m - i;
    for (i = 0, j = 0; i <= n - m;) {
        for (j = 0; j < m; j++)
            if (txt[i + j] != pat[j])
                break;
        if (j == m)
            offset[c++] = i;
        i += next[(unsigned char)txt[i + m]];
    }
    return c;
}

/**************************************************************************
* Rabin-Karp算法查找字符串,时间复杂度最坏是O((n-m+1)m),预处理
* 时间复杂度为Θ(m)。
**************************************************************************/
int rk_search(const char * txt, const char * pat, int offset[])
{
    unsigned int i, j, p, t, h, n, m, c = 0;
    const unsigned int d = 256, q = 16777213;
    n = strlen(txt);
    m = strlen(pat);
    p = (unsigned char)pat[0];
    t = (unsigned char)txt[0];
    for (i = 1, h = 1; i < m; i++) {
        p = (d * p + (unsigned char)pat[i]) % q;
        t = (d * t + (unsigned char)txt[i]) % q;
        h = (h * d) % q;
    }
    for (i = 0; i <= n - m; i++) {
        if (p == t) {
            for (j = 0; j < m; j++)
                if (txt[i + j] != pat[j])
                    break;
            if (j == m)
                offset[c++] = i;
        }
        t = (t + q - (unsigned char)txt[i] * h % q) % q;
        t = (d * t % q + (unsigned char)txt[i + m]) % q;
    }
    return c;
}

/**************************************************************************
* Aho-Corasick算法查找字符串,时间复杂度是O(n),需要预处理
**************************************************************************/
typedef struct TrieNode{
    char c;
    int father;
    int son[26];
    int next;
    int flag;
    int count;
}TrieNode;

int trie_cnt = 0;
TrieNode trie[1000] = {0, {0}, 0, 0};

inline unsigned int ac_char2index(char c)
{
    return c - 'a';
}

void ac_add_string(const char * sub)
{
    int i, j, k, l;
    for (i = 0, j = 0; sub[j] != 0; j++)
    {
        k = ac_char2index(sub[j]);
        if (trie[i].son[k] > 0)
        {
            i = trie[i].son[k];
        }
        else
        {
            l = ++trie_cnt;
            trie[i].son[k] = l;
            trie[l].father = i;
            trie[l].c = sub[j];
            i = l;
        }
    }
    trie[i].flag = 1;
}

void ac_build_automation()
{
    int i, j, k, l;
    for (i = 1; i <= trie_cnt; i++)
    {
        j = trie[i].father;
        k = trie[j].next;
        l = ac_char2index(trie[i].c);
        if (j > 0 && trie[k].son[l] > 0)
            trie[i].next = trie[k].son[l];
    }
}

int ac_search(const char * txt, int offset[])
{
    int i, j, k, l, n, c;
    n = strlen(txt);
    for (i = 0, j = 0, c = 0; i < n; i++) {
        l = ac_char2index(txt[i]);
        while (!trie[j].son[l] && j)
            j = trie[j].next;
        j = trie[j].son[l];
        for (k = j; k; k = trie[k].next)
            if (trie[k].flag)
                offset[c++] = i;
    }
    return c;
}

#endif
/*********************************文件结尾********************************/

你可能感兴趣的:(字符串匹配经典算法)