diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c953ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +CMakeLists.txt \ No newline at end of file diff --git a/lcp_array.h b/lcp_array.h new file mode 100644 index 0000000..d72a497 --- /dev/null +++ b/lcp_array.h @@ -0,0 +1,34 @@ +#ifndef INC_3TERM_CONTESTS_LCP_ARRAY_H +#define INC_3TERM_CONTESTS_LCP_ARRAY_H + + +std::vector make_lcp_array(const std::string& s, const std::vector& suffs){ + std::vector lcp (s.length(), 0); + size_t cur_lcp = 0; + std::vector pos(s.length()); + for(size_t i = 0; i < s.length(); ++i) { + pos[suffs[i]] = i; // pos = suffs^-1 + } + + for (size_t i = 0; i < s.length(); ++i) { + if (cur_lcp > 0) { + --cur_lcp; + } + if (pos[i] == s.length() - 1) { + cur_lcp = 0; + lcp[s.length() - 1] = -1; + } else { + size_t j = suffs[pos[i] + 1]; + while ((i + cur_lcp > j + cur_lcp ? i + cur_lcp : j + cur_lcp) < s.length() + && s[i + cur_lcp] == s[j + cur_lcp]) { + ++cur_lcp; + } + lcp[pos[i]] = cur_lcp; + } + } + + return lcp; +} + + +#endif //INC_3TERM_CONTESTS_LCP_ARRAY_H diff --git a/mod2_a.cpp b/mod2_a.cpp new file mode 100644 index 0000000..3fe5c06 --- /dev/null +++ b/mod2_a.cpp @@ -0,0 +1,40 @@ +/* + * Дана строка длины n. Найти количество ее различных подстрок. Используйте суффиксный массив. + * Построение суффиксного массива выполняйте за O(n log n). + * Вычисление количества различных подстрок выполняйте за O(n). +*/ + + +#include +#include +#include +#include + + +constexpr size_t ALPHABET = 256; + + +size_t find_substrings(std::string& s){ + s += "$"; + std::vector p; + std::vector lcp; + + p = make_suff_array(s); + lcp = make_lcp_array(s, p); + + size_t ans = 0; + for (size_t i = 0; i < s.size(); ++i) { + ans += s.size() - 1 - p[i] - lcp[i]; + } + + return --ans; +} + +int main() { + std::string s; + std::cin >> s; + + std::cout << find_substrings(s); + + return 0; +} diff --git a/suffix_array.h b/suffix_array.h new file mode 100644 index 0000000..8973d39 --- /dev/null +++ b/suffix_array.h @@ -0,0 +1,100 @@ +#ifndef INC_3TERM_CONTESTS_SUFFIX_ARRAY_H +#define INC_3TERM_CONTESTS_SUFFIX_ARRAY_H + + +// сортируем посимвольно строку +std::vector zero_iteration (const std::string &s, const size_t alphabet) { + std::vector suffs (s.length(), 0); // for return + std::vector cnt (alphabet, 0); + + // карманная сортировка подсчетом + for (size_t i = 0; i < s.length(); ++i) { + ++cnt[s[i] - '\0']; + } + // считаем границы + for (size_t i = 1; i < alphabet; ++i) { + cnt[i] += cnt[i - 1]; + } + // suffs будет хранить индексы начал отсортированных подстрок текущей длины + for (size_t i = 0; i < s.length(); ++i) { + suffs[--cnt[s[i] - '\0']] = i; + } + + return suffs; +} + +// создаем классы для последующих итераций +std::vector make_classes (const std::string& s, const std::vector& suffs) { + std::vector classes (s.length(), 0); // for return + size_t class_numb = 0; + char last_char = '$'; + + // каждому суффиксу длины 1 сопоставляем класс + for (size_t i = 0; i < s.length(); ++i) { + if (s[suffs[i]] != last_char){ + last_char = s[suffs[i]]; + ++class_numb; + } + classes[suffs[i]] = class_numb; + } + + return classes; +} + +// сортируем циклические сдвиги строки длины степеней двойки (2, 4, 8, ... n) +void main_iterations (std::vector& suffs, std::vector& classes) { + const size_t s_length = suffs.size(); + std::vector cnt (s_length, 0); + std::vector sorted_by2 (s_length, 0); + std::vector new_classes (s_length, 0); + + + for (size_t cur_len = 1; cur_len <= s_length; cur_len *= 2) { + // сортируем по второй половине подстроки + for (size_t i = 0; i < s_length; ++i){ + sorted_by2[i] = (suffs[i] + s_length - cur_len) % s_length; + } + // сортируем по первой половине + // сортировка устойчивая, значит получим целиком отсортированные подстроки + // обнуляем cnt + for (size_t i = 0; i < s_length; ++i){ + cnt[i] = 0; + } + + for (size_t i = 0; i < s_length; ++i){ + ++cnt[classes[sorted_by2[i]]]; + } + + for (size_t i = 1; i < s_length; ++i) {// считаем границы + cnt[i] += cnt[i - 1]; + } + + for (int i = s_length - 1; i >= 0; --i){ + suffs[--cnt[classes[sorted_by2[i]]]] = sorted_by2[i]; + } + + //подсчтываем классы заново + size_t class_numb = 0; + for (size_t i = 1; i < s_length; ++i){ + size_t mid1 = (suffs[i] + cur_len) % s_length; + size_t mid2 = (suffs[i - 1] + cur_len) % s_length; + if (classes[suffs[i]] != classes[suffs[i - 1]] || classes[mid1] != classes[mid2]) { + ++class_numb; + } + new_classes[suffs[i]] = class_numb; + } + + classes = new_classes; + } +} + + +std::vector make_suff_array (const std::string& s, const size_t alphabet = ALPHABET) { + std::vector suffs = zero_iteration(s, alphabet); + std::vector classes = make_classes(s, suffs); + main_iterations(suffs, classes); + return suffs; +} + + +#endif //INC_3TERM_CONTESTS_SUFFIX_ARRAY_H