1. 程式人生 > 其它 >字串查詢(2) KMP演算法查詢目標字串

字串查詢(2) KMP演算法查詢目標字串

先上程式碼:

class KMP
{
    using DFA_t = vector< vector<int> >;
public:
    KMP(const string &needle)
        : m_NeedleSize{ (int)needle.size() },
          m_DFA(TOTAL_CHAR, std::move(vector<int>(m_NeedleSize, 0)))
    {
        if (m_NeedleSize > 0)
        {
            m_DFA[(unsigned char)needle[0]][0] = 1;
        }

        // generate dfa table by needle str
        for (int i = 1, j = 0; i < m_NeedleSize; i++)
        {
            unsigned char cur = needle[i];
            for (int k = 0; k < TOTAL_CHAR; k++)
            {
                m_DFA[k][i] = m_DFA[k][j];
            }
            m_DFA[cur][i] = i + 1;
            j = m_DFA[cur][j];
        }

        // PrintDFA(m_DFA);
    }

    int SearchIn(const string &haystack) const
    {
        if (m_NeedleSize == 0) return 0;
    
        const int haystackSize { (int)haystack.size() };
        for (int i = 0, j = 0; i < haystackSize; i++)
        {
            j = m_DFA[(unsigned char)haystack[i]][j];
            if (j == m_NeedleSize)
            {
                return i - j + 1;
            }
        }
        return -1;
    }

private:
    static const int TOTAL_CHAR;

    int m_NeedleSize;
    DFA_t m_DFA;
};

const int KMP::TOTAL_CHAR = 256;

leetcode上測試相比暴力字串查詢方法速度快了近10倍
每個字母匹配分三種情況:

  • 匹配到對應字元,模式指標j指向下一個位置.即j+=1;
  • 沒有匹配到對應字元,但是匹配到公共字元字首.這種情況下,根據在公共字元字首中的位置決定模式指標指向的位置
  • 沒有匹配到公共字元字首,這種情況下直接移動模式指標j到模式字串開頭,重新開始匹配

根據上述構建DFA的函式KMP::KMP()可知構造時間複雜度為O(KMP::TOTAL_CHAR * M),空間複雜度為O(KMP::TOTAL_CHAR * M)

M表示模式匹配字串的長度
KMP演算法的優點是對於相同的模式字串,只需要構造一次DFA矩陣
缺點是比較大的空間複雜度

查詢函式KMP::SearchIn時間複雜度為O(N),空間複雜度為O(1)。
綜合看來完全匹配一次時間複雜度為O(KMP::TOTAL_CHAR * M + N),空間複雜度為O(KMP::TOTAL_CHAR * M)