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_c.cpp b/mod2_c.cpp new file mode 100644 index 0000000..deefa8b --- /dev/null +++ b/mod2_c.cpp @@ -0,0 +1,75 @@ +/* + * Заданы две строки s, t и целое число k. + * Рассмотрим множество всех таких непустых строк, которые встречаются как подстроки в s и t одновременно. + * Найдите k-ую в лексикографическом порядке строку из этого множества. + * Полезная статья про сравнение указателей: https://stackoverflow.com/questions/9086372/how-to-compare-pointers. +*/ + + +#include +#include +#include +#include + + +constexpr size_t ALPHABET = 256; + + +bool is_changed (const std::vector& p, const size_t i, const size_t pivot) { + return (p[i] < pivot && p[i - 1] > pivot) || + (p[i] > pivot && p[i - 1] < pivot); +} + + +std::string find_k_substring(std::string& s, const std::string& l, int64_t k){ + const size_t old_s_size = s.size(); + s = s + "#" + l + "$"; + + std::vector p; + std::vector lcp; + p = make_suff_array(s); + lcp = make_lcp_array(s, p); + + size_t lcp_old = 0; + std::string ans; + + for (size_t i = 1; i < s.size(); ++i){ + if (is_changed(p, i, old_s_size)) { + if (lcp_old + k > lcp[i]) { + if (lcp[i] >= lcp_old) { + k -= lcp[i] - lcp_old; + } + lcp_old = lcp[i]; + + }else{ + for (size_t j = p[i]; j < lcp_old + k + p[i]; ++j) { + std::cout << s[j]; + } + + k = 0; + break; + } + } + if (lcp[i] < lcp_old) { + lcp_old = lcp[i]; + } + } + + if (k > 0) { + ans = "-1"; + } + + return ans; +} + +int main() { + std::string s, l; + int64_t k; + std::cin >> s; + std::cin >> l; + std::cin >> k; + + std::cout << find_k_substring(s, l, k); + + 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