WikiSort.ru - Не сортированное

ПОИСК ПО САЙТУ | о проекте

Пре́фикс-фу́нкция от строки и позиции в ней — длина наибольшего префикса подстроки , который одновременно является суффиксом этой подстроки. То есть в начале подстроки длиной нужно найти такой префикс максимальной длины , который был бы суффиксом данной подстроки .

Обозначается ; где  — строка;  — длина подстроки в S. Считают, что .

Часто префикс-функцию определяют в векторной форме:

Пре́фикс-фу́нкция от строки есть вектор , каждый -ый элемент которого равен .

Например, для строки 'abcdabscabcdabia' префикс-функция будет такой: π(abcdabscabcdabia)='0000120012345601'.

Эта функция используется, например, в алгоритме Кнута — Морриса — Пратта.

Алгоритм вычисления

Символы строк нумеруются с 1.

Пусть . Попробуем вычислить префикс-функцию для .

Если , то, естественно, . Если нет — пробуем меньшие суффиксы. Перебирать все суффиксы линейным поиском нет необходимости. Можно воспользоваться уже посчитанными значениями префикс-функции. Можно заметить, что также будет суффиксом строки , так как - длина префикса-суффикса в данной точке. Для любого строка суффиксом не будет. Таким образом, получается алгоритм:

  1. При  — положить .
  2. Иначе при  — положить .
  3. Иначе — установить и перейти к пункту 1.

Для строки 'abcdabscabcdabia' вычисление будет таким:

'a'!='b' => π=0;
'a'!='c' => π=0;
'a'!='d' => π=0;
'a'=='a' => π=π+1=1;
'b'=='b' => π=π+1=2;
'c'!='s' => π=0;
'a'!='c' => π=0;
'a'=='a' => π=π+1=1;
'b'=='b' => π=π+1=2;
'c'=='c' => π=π+1=3;
'd'=='d' => π=π+1=4;
'a'=='a' => π=π+1=5;
'b'=='b' => π=π+1=6;
's'!='i' => π=0;
'a'=='a' => π=π+1=1;

Чуть более интересный пример — для строки 'abcdabcabcdabcdab' вычисление будет таким:

1  S[1]='a', k=π=0;
2  S[2]='b'!=S[k+1] => k=π=0;
3  S[3]='c'!=S[1] => k=π=0;
4  S[4]='d'!=S[1] => k=π=0;
5  S[5]='a'==S[1] => k=π=1;
6  S[6]='b'==S[2] => k=π=2;
7  S[7]='c'==S[3] => k=π=3;
8  S[8]='a'!=S[4] => k:=π(S, 3)=0, S[8]==S[1] => k=π=1;
9  S[9]='b'==S[2] => k=π=2;
10 S[10]='c'==S[3] => k=π=3;
11 S[11]='d'==S[4] => k=π=4;
12 S[12]='a'==S[5] => k=π=5;
13 S[13]='b'==S[6] => k=π=6;
14 S[14]='c'==S[7] => k=π=7;
15 S[15]='d'!=S[8] => k:=π(S, 7)=3, S[15]==S[4] => k=π=4;
16 S[16]='a'==S[5] => k=π=5;
17 S[17]='b'==S[6] => k=π=6;

И результат таков: '00001231234567456'.

Скорость работы

Несмотря на то, что пункт 3 представляет собой внутренний цикл, время вычисления префикс-функции оценивается как . Докажем это.

Все делятся на:

  1. Увеличивающие на единицу. Цикл проходит одну итерацию.
  2. Не изменяющие нулевое . Цикл также проходит одну итерацию. Случаев 1 и 2 в сумме не более штук.
  3. Не изменяющие или уменьшающие положительное . Поскольку внутри цикла значение может только уменьшаться, а увеличение возможно лишь на единицу, то суммарно значение не может уменьшиться более, чем раза, что и ограничивает количество срабатываний внутреннего цикла.

Итого алгоритм требует не более итераций, что доказывает порядок скорости . «Худшим» для алгоритма является случай обработки строки вида 'aa…ab'.

Пример реализации на C++

vector<int> compute_prefix_function(const string& s) 
{
	int len = s.length();
	vector<int> p(len); // значения префикс-функции
	                    // индекс вектора соответствует номеру последнего символа аргумента
	p[0] = 0; // для префикса из нуля и одного символа функция равна нулю

    int k = 0;	
	for (int i = 1; i < len; ++i) {	
		while ((k > 0) && (s[k] != s[i])) 
			k = p[k - 1]; 
		if (s[k] == s[i])
			++k;
		p[i] = k;
	}
	return p;
}

Пример реализации на Delphi

type Arr = array of LongInt;
function compute_prefix_function(s:string):Arr;
var k,i,l:LongInt;
begin
  l := Length(s);
  SetLength(Result, 1 + l);
  Result[0] := 0;
  Result[1] := 0;
  k := 0;
  for i := 2 to l do
  begin
    while (k > 0) and (s[k+1] <> s[i]) do
      k := Result[k];
    if s[k+1] = s[i] then
      Inc(k);
    Result[i] := k;
  end;
end;

Пример реализации на Ruby

def prefix s
  n = s.length-1
  p = Array.new(n,0)
  n.times do |i|
    i+=1
    j = p[i-1]
    while j > 0 && s[i] != s[j]
      j = p[j-1]
    end
    j+=1 if s[i]==s[j]
    p[i] = j
  end
  return p
end

Пример реализации на Java

public static List<Integer> compute_prefix_function(String s) {
    int len = s.length();
    List<Integer> p = new ArrayList<>(); // значения префикс-функции
    for (int i = 0; i < len; ++i) {      // заполнение листа нулями default
        p.add(0);
    }
                                         // индекс листа соответствует номеру последнего символа аргумента
    int k = 0;
    for (int i = 1; i < len; ++i) {
        while ((k > 0) && (s.charAt(k) != s.charAt(i)))
            k = p.get(k - 1);
        if (s.charAt(k) == s.charAt(i))
            ++k;
        p.set(i, k);
    }
    return p;
}

Пример реализации на Python

def prefix(s):
    v = [0]*len(s)
    for i in range(1,len(s)):
        k = v[i-1]
        while k > 0 and s[k] != s[i]:
            k = v[k-1]
        if s[k] == s[i]:
            k = k + 1
        v[i] = k
    return v

Пример реализации на Haskell

prefix :: String -> [Int]
prefix [] = []
prefix str@(_:t) = 
    go 0 0 str t []
  where 
    go vlen _  []       _       v       = reverse v
    go vlen k s@(si:ssi) (sk:ssk) v 
        | si == sk  = go (vlen+1) (k + 1)  ssi    ssk  ((k+1):v)
        | k  == 0   = go (vlen+1)  0       ssi    str  (0:v)
        | otherwise = 
            let
               k' = v!!(vlen-k)
            in
               go  vlen k' s  (drop k'  str)  v

Данная страница на сайте WikiSort.ru содержит текст со страницы сайта "Википедия".

Если Вы хотите её отредактировать, то можете сделать это на странице редактирования в Википедии.

Если сделанные Вами правки не будут кем-нибудь удалены, то через несколько дней они появятся на сайте WikiSort.ru .




Текст в блоке "Читать" взят с сайта "Википедия" и доступен по лицензии Creative Commons Attribution-ShareAlike; в отдельных случаях могут действовать дополнительные условия.

Другой контент может иметь иную лицензию. Перед использованием материалов сайта WikiSort.ru внимательно изучите правила лицензирования конкретных элементов наполнения сайта.

2019-2024
WikiSort.ru - проект по пересортировке и дополнению контента Википедии