Алгоритм быстрее, чем BMH (Бойер – Мур – Хорспул) Поиск - PullRequest
3 голосов
/ 25 мая 2009

Какой алгоритм вы бы использовали для поиска коротких подстрок в коротких текстах? Короче говоря, я имею в виду 5-10 символов для подстроки и 255 для строки. Я думаю о выборе алгоритма в зависимости от длины входных данных. Какой алгоритм лучше подходит для более длинных входов?

Ответы [ 7 ]

5 голосов
/ 25 мая 2009

Попробуйте Turbo-BM . Однако ИМО с такими короткими строками будет достаточно обычного линейного сканирования.

1 голос
/ 18 января 2012

@ анон @Антон Гоголев
Чтобы ответить на ваш вопрос одним словом: Railgun_Quadruplet

Эта функция C так тщательно протестирована на коротких 2,3,4 шаблонах против строк из 160 символов, что, глядя на эту таблицу http://www.sanmayce.com/Railgun/index.html#Heavy-Search-Hustle, вы можете решить самостоятельно.

Статья также на: http://www.codeproject.com/KB/cpp/Railgun_Quadruplet.aspx

1 голос
/ 17 июля 2011

Если вы ищете алгоритм лучше, чем Бойер Мур, то вы спрашиваете смешанный ответ.

Что я знаю, только дерево суффиксов побеждает Бойера Мура в текстовом поиске. Однако на создание индекса уходит больше времени и больше места на диске.

Дополнительная информация: В отношении скорости дерево суффиксов или массив суффиксов превосходит любую версию Boyer Moore, потому что дерево суффиксов в основном реализует все возможные поиски в дереве, подобном структуре данных.

Однако суффикс-деревья имеют высокую стоимость оперативной памяти и медленнее индексируют текст (создают древовидную структуру данных).

Разница в скорости по Бойеру Муру против дерева Суффикс: Бойер Мур линейен по тексту поиска. Дерево суффиксов в шаблоне поиска линейно.

Если вы ищете 5-буквенные слова в тексте из 200 символов, Бойер Мур выполняет 200 операций, а дерево суффиксов - 5.

Как бы быстро это ни было, это очень сложно реализовать. По шкале сложности структур данных это, вероятно, одна из самых сложных. А после сборки он может быть значительно оптимизирован по пространству и скорости.

При этом ищите суффиксное дерево в ближайшие годы. Обычно деревья суффиксов используются для индексации ДНК и оптимизации поисковых систем.

Boyer Moore используется повсеместно, например, в конвенционных программах (функция текстового поиска) и широко используется в поисковых системах.

0 голосов
/ 13 марта 2019

Это другой подход, и может победить BMH. Это похоже на BMH, но продолжает выравнивание последующих текстовых символов после последнего текстового символа и до тех пор, пока текущий текстовый символ не может быть выровнен или пока не останется больше символов для выравнивания. Предварительная обработка создает таблицу следующей позиции любого символа в каждой позиции шаблона. Например:

Pattern:  ABCABC
          012345

Следующий C в позиции 5 сам по себе равен 5, а следующий C в позиции 4 равен 2. Следующая позиция символов, не найденных в шаблоне, равна -1. Ниже приведен код Java:

public class BoyerMooreHorspool {

    int[] tab1 = new int[256];
    int[][] tab2 = new int[1000][1000];
    int[] tab3 = new int[1000];

    public BoyerMooreHorspool() {
        for (int i = 0; i < tab1.length; i++) {
            tab1[i] = 999;
        }
        for (int i = 0; i < 1000; i++) {
            tab2[999][i] = -1;
        }
    }

    public void processPattern(char[] pattern) {
        int i, j, p;

        for (i = 0; i < pattern.length; ++i) {
            tab1[pattern[i]] = -1;
        }

        for (i = 0; i < pattern.length; ++i) {
            tab3[i] = tab1[pattern[i]];
            tab1[pattern[i]] = i;
        }

        for (i = 0; i < pattern.length; i++) {
            p = tab1[pattern[i]];

            for (j = tab3[i] + 1; j < i; j++) {
                tab2[p][j] = tab3[i];
            }
            if (i == p) {
                for (j = i; j < pattern.length; j++) {
                    tab2[p][j] = i;
                }
            } else {
                tab2[p][i] = i;
            }
        }
    }

    public int search(char[] text, char[] pattern) {
        int k = pattern.length - 1;
        int i, j, oi, ok;
        int lpos = k;

        while (k < text.length) {
            j = k;
            ok = k;
            i = tab2[tab1[text[j]]][lpos];
            k += lpos - i;
            j--;
            i--;
            while (i > -1) {
                oi = i;
                i = tab2[tab1[text[j]]][i];
                k += oi - i;
                j--;
                i--;
            }
            if (ok == k) {
                return j + 1;
            }
        }

        return -1;
    }

    public int search(String origText, String origPattern) {
        char[] text = origText.toCharArray();
        char[] pattern = origPattern.toCharArray();

        processPattern(pattern);
        int res = search(text, pattern);

        for (int i = 0; i < pattern.length; i++) {
            tab1[pattern[i]] = 999;
        }

        return res;
    }
}
0 голосов
/ 07 марта 2019

Ниже приведено улучшение моего кода выше. В моем тесте это «шея и шея» с BMH, но в некоторых сериях он значительно выиграл (каждый цикл - 1 000 000 поисков в случайных местах в английском документе с 39 650 символами).

public class BoyerMoore {

    int[] tab1 = new int[256];
    int[][] tab2 = new int[1000][1001];
    int[] tab3 = new int[1000];

    public BoyerMoore8() {
        for (int i = 0; i < tab1.length; i++) {
            tab1[i] = -1;
        }
    }

    public void processPattern(char[] pattern) {
        // tab1 structure,
        // 0 => tab2 index
        // tab2 structure,
        // 0 - pattern length - 2 => skip values
        // pattern length - 1 => default skip value
        // pattern length => pattern position where to start comparing
        int i, p;
        int lp = pattern.length - 1;

        for (i = 0; i < pattern.length; ++i) {
            tab3[i] = tab1[pattern[i]];
            tab1[pattern[i]] = i;
        }

        for (i = pattern.length - 2; i > -1; i--) {
            if (i < tab1[pattern[i]]) {
                continue;
            }

            p = tab1[pattern[i]];
            tab2[p][lp] = lp - i;
            tab2[p][pattern.length] = i - 1;

            computeJump(pattern, p, i + 1, tab2[p][lp]);
        }
        computeJump(pattern, tab1[pattern[lp]], pattern.length, 0);
    }

    public void computeJump(char[] pattern, int index, int len, int defJump) {
        int i, k, x, y;
        int lpos = len - 1;
        int l2pos = len - 2;
        int p = tab3[lpos];

        OUTER1:
        for (k = 0; k < lpos; k++) { // k points to unmatched pattern character
            i = tab3[k + 1];
            OUTER2:
            while (i > 0) {
                if (pattern[i - 1] != pattern[k]) {
                    x = k + 2;
                    y = i + 1;
                    while (x < len) {
                        if (pattern[y++] != pattern[x++]) {
                            i = tab3[i];
                            continue OUTER2;
                        }
                    }
                    tab2[index][k] = (len - y) + defJump;
                    continue OUTER1;
                } else {
                    i = tab3[i];
                }
            }
            x = l2pos - k;
            while (p > x) {
                p = tab3[p];
            }
            i = p;
            OUTER3:
            while (i > -1) {
                x = l2pos;
                y = i - 1;
                while (y > -1) {
                    if (pattern[y--] != pattern[x--]) {
                        i = tab3[i];
                        continue OUTER3;
                    }
                }
                tab2[index][k] = (lpos - i) + defJump;
                continue OUTER1;
            }
            tab2[index][k] = len + defJump;
        }
    }

    public int search(char[] text, char[] pattern) {
        int k = pattern.length - 1;
        int i, j, p;
        int lpos = k;
        int l2pos = k - 1;

        OUTER:
        while (k < text.length) {
            p = tab1[text[k]];
            if (p == -1) {
                k += pattern.length;
                continue OUTER;
            } else {
                if (text[k] == pattern[lpos]) {
                    for (j = l2pos, i = k - 1; j > -1; j--, i--) {
                        if (text[i] != pattern[j]) {
                            k += tab2[p][j];
                            continue OUTER;
                        }
                    }
                    return i + 1;
                } else {
                    for (j = tab2[p][pattern.length], i = k - 1; j > -1; j--, i--) {
                        if (text[i] != pattern[j]) {
                            k += tab2[p][j];
                            continue OUTER;
                        }
                    }
                    k += tab2[p][lpos];
                    continue OUTER;
                }
            }
        }

        return -1;
    }

    public int search(String origText, String origPattern) {
        char[] text = origText.toCharArray();
        char[] pattern = origPattern.toCharArray();

        processPattern(pattern);
        int res = search(text, pattern);

        for (int i = 0; i < pattern.length; i++) {
            tab1[pattern[i]] = -1;
        }

        return res;
    }
}
0 голосов
/ 17 февраля 2019

Я думаю, что Бойер-Мур-Хорспул можно сделать быстрее с помощью этого варианта (я не знаю его имени, может быть, мое имя :-)). Алгоритм BMH не использует совпавшие символы и информацию о несоответствующих символах. Используется только последний текстовый символ в текущей позиции шаблона. Например:

В BMH следующая позиция (C выровнена со следующим C):

Text   : TTTTZBCTTTTTTTTTTTTT
Pattern: ABCCABC
Text   : TTTTZBCTTTTTTTTTTTTT
Pattern:    ABCCABC

Если мы используем совпавшие символы, следующая позиция (BC выравнивается относительно следующего BC):

Text   : TTTTZBCTTTTTTTTTTTTT
Pattern: ABCCABC
Text   : TTTTZBCTTTTTTTTTTTTT
Pattern:     ABCCABC

Если мы используем совпавшие символы и непропаренный символ, следующая позиция (BC выравнивается по отношению к следующему BC, но поскольку символ шаблона, который не соответствует, то есть A, совпадает с предыдущим символом следующего BC, это также не будет совпадать. Так как нет другого BC, пропускается длина шаблона):

Text   : TTTTZBCTTTTTTTTTTTTT
Pattern: ABCCABC
Text   : TTTTZBCTTTTTTTTTTTTT
Pattern:        ABCCABC

Это реализация Java (все еще может быть улучшена), используйте на свой страх и риск, потому что я не проверил ее полностью. В моих тестах производительности Бойер-Мур-Хорспул по-прежнему побеждает эту реализацию. Но, как и ожидалось, если шаблон используется повторно (без предварительной обработки шаблона для обоих), эта реализация выигрывает.

public static int[] processPattern1(char[] pattern) {
    int[] skip = new int[256];

    for (int i = 0; i < skip.length; ++i) {
        skip[i] = pattern.length;
    }

    for (int i = 0; i < pattern.length - 1; ++i) {
        skip[pattern[i]] = pattern.length - i - 1;
    }

    return skip;
}

public static int[] processPattern2(char[] pattern) {
    int[] skip = new int[pattern.length];
    int i, j, k, x, y;
    int lpos = pattern.length - 1;
    int l2pos = pattern.length - 2;

    OUTER:
    for (k = l2pos; k > -1; k--) { // k points to unmatched pattern character
        j = k + 1;
        for (i = k; i > 0; i--) {
            if (pattern[i] == pattern[j] && pattern[i-1] != pattern[k]) {
                for (x = i + 1, y = j + 1; y < pattern.length && pattern[x] == pattern[y]; x++, y++) {
                }
                if (y == pattern.length) {
                    skip[k] = pattern.length - x;
                    continue OUTER;
                }
            }
        }
        for (x = lpos - j; x > -1; x--) {
            if (pattern[x] == pattern[lpos]) {
                for (i = x - 1, y = l2pos; i > -1 && pattern[i] == pattern[y]; i--, y--) {
                }
                if (i == -1) {
                    skip[k] = lpos - x;
                    continue OUTER;
                }
            }
        }
        skip[k] = pattern.length;
    }

    return skip;
}

public static int search(char[] text, char[] pattern, int[] skip1, int[] skip2) {
    int k = pattern.length - 1;
    int i, j;
    int lpos = k;
    int l2pos = pattern.length - 2;

    while (k < text.length) {
        if (text[k] == pattern[lpos]) {
            for (j = l2pos, i = k - 1; j > -1 && text[i] == pattern[j]; j--, i--) {
            }
            if (j == -1) {
                return i + 1;
            }
            k += skip2[j];
        } else {
            k += skip1[text[k]];
        }
    }

    return -1;
}

public static void main(String[] args) {
    String origText = "TTTTTTTTTTTTTTTZBCRABCCABCTTTTTTTTTTTTT";
    char[] pattern = "ZBCRABCCABC".toCharArray();
    char[] text = origText.toCharArray();
    int[] skip1 = processPattern1(pattern);
    int[] skip2 = processPattern2(pattern);
    System.out.println(search(text, pattern, skip1, skip2) == origText.indexOf(pattern));
}
0 голосов
/ 25 мая 2009

Вы можете попробовать Суффикс-деревья или Суффикс-массивы . Оба зависят от длины шаблона.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...