KMP
文字列照合について読んでいます。
プレフィックステーブルを作成して、パターンを前処理する必要があります。
たとえば、文字列ababaca
の場合、プレフィックステーブルは次の ようになります。P = [0, 0, 1, 2, 3, 0, 1]
しかし、数字が何を示しているのかわかりません。パターンがシフトするときにパターンの一致を見つけるのに役立つと読みましたが、この情報を表の数字と関連付けることはできません。
6 に答える
すべての番号は対応するプレフィックス( "a"、 "ab"、 "aba"、...)に属し、各プレフィックスについて、プレフィックスと一致するこの文字列の最長のサフィックスの長さを表します。ここでは、文字列全体を接尾辞または接頭辞としてカウントしません。これは、自己接尾辞および自己接頭辞と呼ばれます(少なくともロシア語では、英語の用語についてはわかりません)。
つまり、文字列「ababaca」があります。それを見てみましょう。KMPは、空でないプレフィックスごとにプレフィックス関数を計算します。プレフィックス関数としてs[i]
、文字列として定義しましょう。p[i]
プレフィックスとサフィックスが重複する場合があります。
+---+----------+-------+------------------------+
| i | s[0:i] | p[i] | Matching Prefix/Suffix |
+---+----------+-------+------------------------+
| 0 | a | 0 | |
| 1 | ab | 0 | |
| 2 | aba | 1 | a |
| 3 | abab | 2 | ab |
| 4 | ababa | 3 | aba |
| 5 | ababac | 0 | |
| 6 | ababaca | 1 | a |
| | | | |
+---+----------+-------+------------------------+
文字列Sのプレフィックス関数を計算する単純なC++コード:
vector<int> prefixFunction(string s) {
vector<int> p(s.size());
int j = 0;
for (int i = 1; i < (int)s.size(); i++) {
while (j > 0 && s[j] != s[i])
j = p[j-1];
if (s[j] == s[i])
j++;
p[i] = j;
}
return p;
}
このコードは最短ではないかもしれませんが、コードの流れを理解するのは簡単です。プレフィックスを計算するための単純なJavaコード-配列-
String pattern = "ababaca";
int i = 1, j = 0;
int[] prefixArray = new int[pattern.length];
while (i < pattern.length) {
while (pattern.charAt(i) != pattern.charAt(j) && j > 0) {
j = prefixArray[j - 1];
}
if (pattern.charAt(i) == pattern.charAt(j)) {
prefixArray[i] = j + 1;
i++;
j++;
} else {
prefixArray[i] = j;
i++;
}
}
for (int k = 0; k < prefixArray.length; ++k) {
System.out.println(prefixArray[k]);
}
必要な出力を生成します-
0 0 1 2 3 0 1
Pythonの実装
p='ababaca'
l1 = len(p)
j = 0
i = 1
prefix = [0]
while len(prefix) < l1:
if p[j] == p[i]:
prefix.append(j+1)
i += 1
j += 1
else:
if j == 0:
prefix.append(0)
i += 1
if j != 0:
j = prefix[j-1]
print prefix
string text = "ababbabbababbababbabb"; static int arr [30];
int i = 1;
while (i < text.length())
{
int j = 0;
int value = 0;
while (((i + j) < text.length()) && (text[j] == text[i + j]))
val[i + j] = ++value, j++;
i += j + 1;
}
val[]に保存されている必要な出力
私はJavascriptを使って手を試しました。提案のために開いてください。
const prefixArray = function (p) {
let aux = Array(p.length).fill(0);
// For index 0 the matched index will always be 0, so we will we start from 1
let i = 1;
let m = 0; // mismatched index will be from 0th
// run the loop on pattern length
while ( i < p.length) {
// 3 Cases here
// First when we have a match of prefix and suffix of pattern
if(p.charAt(i) === p.charAt(m)) {
// increment m
m++;
// update aux index
aux[i] = m;
// update the index.
i++;
}
// Now if there is no match and m !=0 means some match happened previously
// then we need to move back M to that index
else if(p.charAt(i) !== p.charAt(m) && m !== 0) {
m = aux[m-1];
// we dont want to increment I as we want to start comparing this suffix with previous matched
} else {
// if none of the above conditions then
// just update the current index in aux array to 0
aux[i] = 0; // no match
i++; // shift to the next char
}
}
return aux;
}
オフセットなしバージョン
これは、私がtodoインデックスと呼んでいるもののアイデアに基づいています:
int confix[1000000];
void build_confix(char *pattern) {
// build len %
int len_pat = strlen(pattern);
// i, j using todo-indexing.
int j, i;
confix[j = 1] = i = 0;
while (j < strlen(pattern)) {
whlie (i && pattern[j] != pattern[i])
// length -> length mapping, no offset
i = confix[i];
confix[++j] = pattern[j] == pattern[i]?
++i:
0;
}
}
confix[]
次に、このテーブルを使用needle
して、middle(test
)内のsを見つけることができます。
int kmp_find_first(char *test, char *needle) {
int j = 0, i = 0;
while (j < strlen(test)) {
while (i && test[j] != needle[i])
i = confix[i];
++j; test[j] == needle[i]?
++i:
0;
if (i == strlen(needle))
return j - strlen(needle);
}
return -1;
}