Engineering Note

プログラミングなどの技術的なメモ

KMP法 (Pythonによるアルゴリズムとデータ構造)

brute forcing

本記事は、ソフトバンクパブリッシングから発行されている「定本 Cプログラマのためのアルゴリズムとデータ構造 (SOFTBANK BOOKS)」を参考にPythonアルゴリズムとデータ構造について学習していきます。

今回は、文字列の探索としてKMP法(Knuth–Morris–Pratt Algorithm)について学んでいきます。

 

 

KMP法とは

KMP法とは、1970年にS.A.Cookの文字列探索アルゴリズムの証明を基に、D.E.KnuthとV.R.Prattが発見したアルゴリズムで、さらに同時期にテキストエディタ を作成する中でJ.H.Morrisが同じようなアルゴリズムを発見し、彼らの名前をとってKMP法(Knuth–Morris–Pratt Algorithm)と呼ばれています。

 

力まかせのアルゴリズムでは、パターンが途中までは一致していても、パターンとの不一致が起きたら、問答無用で文字列側のポインタを戻してしまっていました。

KMP法では上記の無駄を無くし、文字列側のポインタを戻すことはしません。

またパターン側では、不一致が起きた時にどれだけパターンをずらせばよいかを参照するための表も作成します。

 

なお、KMP法では計算量が最悪でもO(n)になりますが、条件分岐などの定数項が多くなるため、場合によっては力まかせのアルゴリズムより遅くなることもあります。 

 

PythonでKMP法を実装してみる

それでは、PythonでKMP法を実装してみます。

 

# kmp.py
import time

def create_table(pattern):
    table = [0 for _ in range(len(pattern))]
    j = 0
    for i in range(1, len(pattern)):
        if pattern[i] == pattern[j]:
            j += 1
            table[i] = j
        else:
            table[i] = j
            j = 0
    return table

def kmp_search(string, pattern):
    table = create_table(pattern)
    i = j = 0
    while i < len(string) and j < len(pattern):
        if string[i] == pattern[j]:
            i += 1
            j += 1
        elif j == 0:
            i += 1
        else:
            j = table[j]

    if j == len(pattern):
        return i - j
    else:
        return None

with open("sample_string.txt", "r") as f:
    string = f.read()
pattern = "hoge"

start = time.time()

index = kmp_search(string, pattern)
if index:
    print("The pattern starts with at index {}.".format(index))
else:
    print("No pattern matched.")
print("elapsed {} sec.".format(time.time() - start))

 

動作確認 

それでは上記で作成したスクリプトを実行してみます。

事前にa~zまでランダムに100,000文字を生成し、文字列の最後に"hoge"を追記したものから、"hoge"のパターンを探索します。

自環境で実行した結果を以下にまとめます。

 

100,000文字の探索結果
アルゴリズム 所要時間(秒)
Python 組込みメソッド(find()) 0.0004
Brute-forcing 0.0414
KMP 0.0379

 

参考書籍

定本 Cプログラマのためのアルゴリズムとデータ構造 (SOFTBANK BOOKS)