Алгоритм Апостолико-Крочемора — различия между версиями
|  (→Псевдокод) | |||
| Строка 13: | Строка 13: | ||
| }} | }} | ||
| − | Введем обозначение: пусть <tex>t[i]</tex> {{---}} длина наибольшего бордера для <tex>x[0 .. i - 1]</tex> за которым следует символ <tex>c \neq x[i]</tex> и <tex>-1</tex> если нет такого помеченного бордера, где <tex>0 < i \ | + | Введем обозначение: пусть <tex>t[i]</tex> {{---}} длина наибольшего бордера для <tex>x[0 .. i - 1]</tex> за которым следует символ <tex>c \neq x[i]</tex> и <tex>-1</tex> если нет такого помеченного бордера, где <tex>0 < i \leqslant m</tex> (<tex>t[0] = -1</tex>). Затем, после сдвига, сравнение можно продолжить между символами <tex>x[t[i]]</tex> и <tex>y[i + j]</tex> не потеряв  никакого вхождения <tex>x</tex> в <tex>y</tex> и избежав отступа по тексту (смотри рисунок ниже). | 
| Строка 23: | Строка 23: | ||
| Во время поиска вхождений мы рассматриваем данную тройку <tex>(i, j, k)</tex> где: | Во время поиска вхождений мы рассматриваем данную тройку <tex>(i, j, k)</tex> где: | ||
| * шаблон сравнивается с <tex>y[j, \ldots , j + m - 1]</tex> | * шаблон сравнивается с <tex>y[j, \ldots , j + m - 1]</tex> | ||
| − | * <tex>0 \ | + | * <tex>0 \leqslant k \leqslant l</tex> и <tex>x[0, \ldots, k - 1] {{=}} y[j, \ldots , j + k - 1]</tex> | 
| − | * <tex>l \ | + | * <tex>l \leqslant i < m</tex> и <tex>x[l, \ldots, i - 1] {{=}} y[j + l, \ldots , i + j - 1]</tex> | 
| Вначале инициализируем эту тройку <tex>(l, 0, 0)</tex>. | Вначале инициализируем эту тройку <tex>(l, 0, 0)</tex>. | ||
| Теперь опишем, как по уже вычисленной тройке <tex>(i, j, k)</tex> перейти к следующей. | Теперь опишем, как по уже вычисленной тройке <tex>(i, j, k)</tex> перейти к следующей. | ||
| Строка 35: | Строка 35: | ||
| #: Если <tex>x[i] {{=}} y[i + j]</tex>, тогда следующая тройка <tex>(i + 1, j, k)</tex>. | #: Если <tex>x[i] {{=}} y[i + j]</tex>, тогда следующая тройка <tex>(i + 1, j, k)</tex>. | ||
| #: Если <tex>x[i] \neq y[i + j]</tex>, тогда возможны два случая в зависимости от значения <tex>t[i]</tex>: | #: Если <tex>x[i] \neq y[i + j]</tex>, тогда возможны два случая в зависимости от значения <tex>t[i]</tex>: | ||
| − | #:* Если <tex>t[i] \ | + | #:* Если <tex>t[i] \leqslant l</tex>, тогда следующая тройка <tex>(l, i + j - t[i], \max(0, t[i]))</tex>. | 
| #:* Если <tex>t[i] > l</tex>, тогда следующая тройка <tex>(t[i], i + j - t[i], l)</tex>. | #:* Если <tex>t[i] > l</tex>, тогда следующая тройка <tex>(t[i], i + j - t[i], l)</tex>. | ||
| # <tex>i = m</tex>: | # <tex>i = m</tex>: | ||
Версия 19:31, 5 марта 2016
Алгоритм Апостолико — Крочемора (англ. Apostolico — Crochemore algorithm) — алгоритм поиска подстроки в строке.
Содержание
Описание алгоритма
Нам даны: — текст, — образец, , .
Для начала рассмотрим ситуацию, когда мы сравниваем наш образец с . Предположим, что первое несовпадение произойдет между и при . Тогда и . Когда сдвиг возможен, разумно ожидать, что префикс шаблона совпадет c некоторым суффиксом . Более того, если мы хотим избежать несовпадения при сдвиге, то нужно, чтобы символ, следующий за префиксом в шаблоне, не совпадал с . Такой наибольший префикс называется помеченным бордером строки .
| Определение: | 
| Помеченный бордер (англ. tagged border) строки — строка . | 
Введем обозначение: пусть  — длина наибольшего бордера для  за которым следует символ  и  если нет такого помеченного бордера, где  (). Затем, после сдвига, сравнение можно продолжить между символами  и  не потеряв  никакого вхождения  в  и избежав отступа по тексту (смотри рисунок ниже).
Пусть теперь , если  и , иначе  равно позиции первого элемента, который не равен  (, где  и , а  и ). На каждой итерации алгоритма мы выполняем сравнения с шаблоном в следующем порядке: .
Во время поиска вхождений мы рассматриваем данную тройку где:
- шаблон сравнивается с
- и
- и
Вначале инициализируем эту тройку . Теперь опишем, как по уже вычисленной тройке перейти к следующей. Возможны три случая в зависимости от значения :
-  :
- Если , тогда следующая тройка .
- Если , тогда следующая тройка .
 
-  
- Если , тогда следующая тройка .
-  Если , тогда возможны два случая в зависимости от значения :
- Если , тогда следующая тройка .
- Если , тогда следующая тройка .
 
 
-  :
- Если и , тогда следующая тройка .
- Иначе либо и , либо . Если , то вхождение в найдено. В обоих случаях следующая тройка вычисляется, как в случае .
 
Псевдокод
void getT(string x, int t[]):
    int i = 0
    int j = t[0] = -1
    while i < x.size 
        while j > -1 and x[i]  x[j]
            j = t[j]
        i++
        j++
        if x[i] == x[j]
            t[i] = t[j]
        else
            t[i] = j
   
void aG(string x, string y):
    int l, t[x.size]
    //предподсчет  вычисление массива 
    getT(x, t)
    for l = 1; x[l - 1] == x[l]; l++
        if l == x.size 
            l = 0
    //поиск  вычисление позиций вхождения  в 
    int i = l
    int j = 0
    int k = 0
    while j  y.size - x.size 
        while i < x.size and x[i] == y[i + j]
            ++i
        if i  x.size 
            while k < l and x[k] == y[j + k]
                ++k
            if k  l
                OUTPUT(j)
        j += i - t[i]
        if i == l
            k = max(0, k - 1)
        else
            if t[i]  l 
                k = max(0, t[i])
                i = l
            else
                k = l
                i = t[i]
Асимптотика алгоритма
Этап предподсчета, а именно вычисление массива и переменной занимает времени и константное количество памяти. Этап поиска занимает времени, более того, алгоритм в худшем случае выполнит сравнений.


