Я реализую поиск шаблона байтов KMP с поддержкой подстановочных знаков. Ниже приведен алгоритм генерации таблицы префиксов. БЕЗ подстановочные:
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;
}
Проблема с алгоритмом выше заключается в том, что он не работает с подстановочными знаками. Короче говоря, рассмотрим следующее математический уравнения:
a = b
b = c
a = c
Эти уравнения на 100% верны, когда не используются подстановочные знаки. Однако они не применяются к групповым символам. EX: a = 1
, b = ??
(подстановочный знак) и c = 2
, a equals b
а также b equals c
, но a does not equal to c
Из-за этого странного свойства групповых символов алгоритм, упомянутый выше, не будет работать! Следовательно, я должен реализовать специальный алгоритм для подстановочных знаков.
Моя текущая реализация выглядит следующим образом:
vector<int> GeneratePrefixTable(vector<int> bytes, vector<unsigned char> flags)
{
vector<int> prefixTable(bytes.size());
prefixTable[0] = 0;
for (int j = 1, m = bytes.size(); j < m; j++)
{
int largest = 0;
for (int i = 1; i < j; i++)
{
bool match = true;
for (int k = 0; k < i; k++)
{
if (bytes[k] != bytes[j - i + k + 1] && !flags[k] && !flags[j - i + k + 1])
{
//if the bytes do not match and neither of them is a wildcard
match = false;
break;
}
}
if (!match)
{
continue;
}
largest = i;
}
prefixTable[j] = largest;
}
return prefixTable;
}
определения переменных:
vector<int> bytes
шаблон. иначе игла.vector<unsigned char> flags
массив флагов, чтобы указать, является ли байт в определенном месте подстановочным знакомj
индекс шаблона, который мы смотрим на генерацию префикса #largestOne
длина наибольшего префикса, найденного до сих порi
длина префикса, который мы тестируемОбратите внимание, что я не остановил поиск, как только нашел неработающую длину префикса. Это также связано со странным свойством подстановочного знака.
Например, рассмотрим шаблон 01 02 ?? 02 00
:
1
суффикс:0
, несоответствие1 2
суффикс:2 0
несоответствие1 2 ??
суффикс: ?? 2 0
матч!Из-за этого странного свойства я должен проверять каждую возможную длину префикса. Это замедляет мой алгоритм еще больше. Какими способами, с точки зрения алгоритма и реализации, можно ускорить алгоритм генерации префиксной таблицы?
Вы путаете два типа. Тип «символьный» содержит на один элемент меньше, чем тип «шаблон поиска, символ или подстановочный знак».
Это заставляет вас ошибочно предполагать, что «а» соответствует «??» (Групповой символ). Это не так. Шаблон «а» соответствует символу «а», шаблон «??» соответствует любому персонажу. Явно разные закономерности.
Это помогает рассмотреть шаблоны регулярных выражений. [ab]
такой же шаблон, как [ba]
; [0-9]
такой же шаблон, как [0123456789]
но a
определенно НЕ та же самая картина как .
,
Паттерны транзитивно равны. Опять же, используя стиль регулярных выражений: [abc]
знак равно [acb]
знак равно [bca]
,
KMP требует от вас быть осторожным в отношении различий. Вам необходимо определить, какова следующая возможная позиция совпадения, учитывая текущую позицию совпадения и длину совпадающего префикса. Это не связано с шаблоном равенство. Это включает в себя пересечение шаблонов.
Небольшой боковой шаг: шаблон определяет набор строк, которые должны быть приняты. Можно сказать, что два шаблона имеют пересечение, которое представляет собой набор строк, которые оба принимают. Это сложно рассчитать для сложных моделей.
На практике KMP сравнивает найденный префикс поиска со сдвинутой версией искомого слова. Сохраненное значение — это наименьшее смещение, для которого два шаблона имеют непустое пересечение.
Других решений пока нет …