rkMatch ラビン-カープによる文字列照合アルゴリズム rkHash 照合パターンのハッシュ関数値
文字列照合 char *rkMatch(char *text, int len, int patlen, unsigned long patHash, unsigned long dm); 照合パターンのハッシュ関数値 unsigned long rkHash(unsigned long *dm, char *pattern, int patlen);
文字列照合関数において text (入力)テキスト len (入力)テキストの長さ patlen (入力)照合パターンの長さ patHash (入力)照合パターンのハッシュ関数値 dm (入力)256m-1 % q の値 照合パターンのハッシュ関数値の関数において dm (入出力)256m-1 % q の値 pattern (入力)照合パターン patlen (入力)照合パターンの長さ
文字列照合関数について 照合パターンがテキストから見つかった場合はそのポインタ、 見つからなかった場合は 0 (NULL)。 照合パターンのハッシュ関数値の関数において 照合パターンのハッシュ関数値
rkPattern() を呼び出して照合パターンのハッシュ関数値を算出した後、 文字列照合関数 rkMatch() を利用すること。
#define Q 33554393L /* prime number */ #define LOG2D 8 /* 2^8 = 256 */ unsigned long rkHash(unsigned long *dm, char *pattern, int patlen) { int i; unsigned long h1; for (i = 1, *dm = 1; i < patlen; i++) *dm = (*dm << LOG2D) % Q; for (i = 0, h1 = 0; i < patlen; i++) h1 = ((h1 << LOG2D) + pattern[i]) % Q; return h1; } char *rkMatch(char *text, int len, int patlen, unsigned long patHash, unsigned long dm) { int i; char *textEnd; unsigned long h2; if (patlen == 0) return text; if (len < patlen) return NULL; for (i = 0, h2 = 0; i < patlen; i++) h2 = ((h2 << LOG2D) + text[i]) % Q; textEnd = text + (len - patlen) + 1; while (text != textEnd) { if (h2 == patHash) return text; h2 = (h2 + (Q << LOG2D) - *text * dm) % Q; h2 = ((h2 << LOG2D) + *(text + patlen)) % Q; text++; } return NULL; }
この方法ではまず照合パターンのハッシュ関数値 h1 を、つぎにテキスト 最初の m 文字分のハッシュ関数値 h2 を計算する。そのあとテキスト上 の照合開始位置を右にずらしながら、ハッシュ関数値を漸化式によって計 算し、その値と h1 との比較を繰り返す。
この方法の計算量 O(m+n) である。違う文字列が同じハッシュ関数値をもつ ことは理論的には否定できないが、大きな素数 q を選べば実用上その問題 を無視してよい。