Pythonでのクヌース・モリス・プラット法の実装

Pythonでのクヌース・モリス・プラット法の実装クヌース・モリス・プラット法 (Knuth-Morris-Pratt algorithm) は、部分文字列検索において、与えられた文字列の中に特定のパターンが現れるかどうかを効率的に調べるアルゴリズムです。このアルゴリズムは、O(n + m) の時間計算量を持ち、n は検索対象の文字列の長さ、m は検索するパターンの長さです。ここでは、Pythonを用いてクヌース・モリス・プラット法を実装する方法について説明します。

クヌース・モリス・プラット法とは

まず、クヌース・モリス・プラット法の概要を説明します。このアルゴリズムは、検索するパターンのプレフィックス (接頭辞) とサフィックス (接尾辞) の一致に着目します。例えば、"ABCDABD" という文字列において、パターン "ABD" の場合、プレフィックス "A" とサフィックス "D" の一致を探すことで、次に比較するべき位置を決定します。

Pythonでのクヌース・モリス・プラット法の実装

次に、Pythonでの実装方法について説明します。以下は、クヌース・モリス・プラット法を実装した関数の例です。

def kmp_search(pattern, text):
    m = len(pattern)
    n = len(text)
    lps = [0] * m
    j = 0
    compute_lps_array(pattern, m, lps)
    i = 0
    while i < n:
        if pattern[j] == text[i]:
            i += 1
            j += 1
        if j == m:
            print("Found pattern at index " + str(i-j))
            j = lps[j-1]
        elif i < n and pattern[j] != text[i]:
            if j != 0:
                j = lps[j-1]
            else:
                i += 1

def compute_lps_array(pattern, m, lps):
    len = 0
    i = 1
    while i < m:
        if pattern[i] == pattern[len]:
            len += 1
            lps[i] = len
            i += 1
        else:
            if len != 0:
                len = lps[len-1]
            else:
                lps[i] = 0
                i += 1

この関数では、引数としてパターン (pattern) と検索対象の文字列 (text) を受け取り、パターンが現れた場所のインデックスを表示します。lps (Longest Proper Prefix Suffix) は、プレフィックスサフィックスの一致を示す配列で、compute_lps_array関数によって事前に計算されます。関数内では、iはtextのインデックス、jはpatternのインデックスを表し、iとjを1つずつ進めながら、一致する文字があるかどうかを調べます。jがパターンの長さmと等しくなると、パターンが現れた場所のインデックスを表示し、jをlps[j-1]で更新します。このようにすることで、パターンの一部が一致しなかった場合でも、以前に一致していた部分の情報を利用して、比較をスキップできるようになっています。

compute_lps_array関数では、パターンのプレフィックスサフィックスの一致を示すlps配列を計算します。iはパターンのインデックスを表し、lenはlps[i]の値を保持します。iを1つずつ進めながら、パターンの文字列を比較して、一致している場合はlenをインクリメントし、lps[i]にlenの値を代入します。一致していない場合は、lenを更新するか、lps[i]に0を代入します。以上が、Pythonでのクヌース・モリス・プラット法の実装についての説明です。以下は、この関数を使用した例です。

text = "ABCABCDABDABCABCDABDABCDABDE"
pattern = "ABD"
kmp_search(pattern, text)

この例では、パターン "ABD" がテキスト文字列の中で2箇所に現れています。関数の出力は次のようになります。

Found pattern at index 3
Found pattern at index 18

このように、クヌース・モリス・プラット法を用いることで、文字列の中から特定のパターンを効率的に検索することができます。

完全なコードの例

以下に、完全なPythonコードを示します。

def compute_lps_array(pattern):
    """
    Compute the LPS (Longest Prefix Suffix) array for the given pattern
    """
    m = len(pattern)
    lps = [0] * m
    len = 0
    i = 1
    while i < m:
        if pattern[i] == pattern[len]:
            len += 1
            lps[i] = len
            i += 1
        else:
            if len != 0:
                len = lps[len-1]
            else:
                lps[i] = 0
                i += 1
    return lps

def kmp_search(pattern, text):
    """
    Search for occurrences of the given pattern in the given text using the KMP algorithm
    """
    n = len(text)
    m = len(pattern)
    lps = compute_lps_array(pattern)
    i = 0  # index for text[]
    j = 0  # index for pattern[]
    while i < n:
        if pattern[j] == text[i]:
            i += 1
            j += 1
        if j == m:
            print("Found pattern at index", i-j)
            j = lps[j-1]
        elif i < n and pattern[j] != text[i]:
            if j != 0:
                j = lps[j-1]
            else:
                i += 1

text = "ABCABCDABDABCABCDABDABCDABDE"
pattern = "ABD"
kmp_search(pattern, text)

このコードでは、compute_lps_array関数でLPS配列を計算し、kmp_search関数でテキスト文字列の中からパターンを検索します。最後に、テスト用の文字列とパターンを指定して、kmp_search関数を呼び出します。

まとめ

以上が、Pythonでのクヌース・モリス・プラット法の実装と使用例についての説明です。このアルゴリズムを用いることで、文字列の中から特定のパターンを高速に検索することができます。

問題解決のための「アルゴリズム×数学」が基礎からしっかり身につく本 [ 米田優峻 ]