//Суффиксный массив //Реализация за O(n*log(n)) (Алгоритм Карпа-Миллера-Розенберга) //Код проверялся на компиляторе g++-4.3.2 //Автор - Золотов Алексей //25 марта 2009 #include #include #include using namespace std; //сколько символов в работе - [0;L-1] const int L=256; //построение суффиксного массива vector getarr(string s) { //s - исходная строка //суффиксный массив vector arr; arr.resize(s.size()); //массив цветов vector col; col.resize(s.size()); //массив для временных данных vector buf; buf.resize(s.size()); //массив для карманов сортировки vector buck; buck.resize(max(L, (int) s.size())); //Шаг первый - начальная сортировка //мы хотим отсортировать буквы строки //посчитаем количество всех букв for (int i = 0; i < (int) s.size(); i++) buck[s[i]]++; //преобразуем массив так, чтобы каждый элемент указывал на положение в массиве первой данной буквы int sum = 0; for (int i = 0; i < L; i++) sum += buck[i], buck[i] = sum - buck[i]; //теперь заполним массив arr: Теперь в нем суффиксы отсортированы по первой букве for (int i = 0; i < (int) s.size(); i++) arr[buck[s[i]]++] = i; //теперь проставляем цвета: цвет увеличивается на 1 если следующая буква - другая col[arr[0]] = 0; for (int i = 1; i < (int) s.size(); i++) col[arr[i]] = col[arr[i-1]] + (s[arr[i]] != s[arr[i-1]]); int cn = col[arr[s.size() - 1]] + 1; //Шаг второй - постепенное расширение подстрок //в начале цикла отсортированы подстроки длины l, а в конце - длины 2l for (int l = 1; l < (int) s.size(); l *= 2) { //обнуляем массив buck и заполняем для сортировки по col for (int i = 0; i < (int) s.size(); i++) buck[i] = 0; for (int i = 0; i < (int) s.size(); i++) buck[col[i]]++; sum = 0; for (int i = 0; i < cn; i++) sum += buck[i], buck[i] = sum - buck[i]; //строим новый массив в buf (не забываем сдвинуть указатель по модулю на l влево), затем копируем его в arr for (int i = 0; i < (int) s.size(); i++) buf[buck[col[(arr[i] - l + s.size()) % s.size()]]++]=(arr[i] - l + s.size()) % s.size(); arr = buf; //теперь перекрашиваем массив col: заполняем массив buf, увеличиваем цвет на единицу если один из цветов отличается, затем копируем buf[arr[0]] = 0; for (int i = 1; i < (int) s.size(); i++) buf[arr[i]] = buf[arr[i - 1]] + (col[arr[i]] != col[arr[i - 1]] || col[(arr[i] + l) % s.size()] != col[(arr[i - 1] + l) % s.size()]); cn = buf[arr[s.size() - 1]] + 1; col = buf; } //возвращаем результат return arr; } //построение LCP vector getlcp(string s, vector arr) { //обратный суффиксный массив vector obr; obr.resize(s.size()); //наибольшый общий предок vector lcp; lcp.resize(s.size()); //строим обратный суффиксный массив for (int i = 0; i < (int) s.size(); i++) obr[arr[i]] = i; //текущее значение int tek = 0; //i - номер суффикса, цикл по уменьшению длины суффикса for (int i = 0; i < (int) s.size() - 1; i++) { //пока символы суффиксов совпадают - увеличиваем длину while (s[i + tek] == s[arr[obr[i] + 1] + tek]) tek++; //присваиваем lcp и уменьшаем tek на 1 lcp[obr[i]] = tek--; //проверяем на выход за пределы if (tek < 0) tek = 0; } //возвращаем результат return lcp; } //основная программа int main () { //рабочая строка string s; //считываем cin >> s; //приписываем конечный символ s += char(0); //считаем массив и lcp vector arr = getarr(s), lcp = getlcp(s, arr); //выводим результат for (int i = 0; i < (int) s.size(); i++) cout << arr[i] << ' ' << lcp[i] << ' ' << s.substr(arr[i], s.size() - arr[i]) << endl; //завершаем работу return 0; }