From 6805423c7f4af20ddb5153ed5f29f4fb9b3fc8de Mon Sep 17 00:00:00 2001 From: SweetWeakness Date: Sat, 9 Nov 2019 00:42:31 +0300 Subject: [PATCH 1/5] 1st and 2nd module --- .gitignore | 1 + 1st module/mod1_a.cpp | 49 ++++++++++++ 1st module/mod1_b1.cpp | 45 +++++++++++ 2nd module/mod2_a.cpp | 125 ++++++++++++++++++++++++++++++ 2nd module/mod2_c.cpp | 170 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 390 insertions(+) create mode 100644 .gitignore create mode 100644 1st module/mod1_a.cpp create mode 100644 1st module/mod1_b1.cpp create mode 100644 2nd module/mod2_a.cpp create mode 100644 2nd module/mod2_c.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/1st module/mod1_a.cpp b/1st module/mod1_a.cpp new file mode 100644 index 0000000..1c2dbeb --- /dev/null +++ b/1st module/mod1_a.cpp @@ -0,0 +1,49 @@ +#include +#include + + +void prefix_function (std::string& s, std::vector& pi) { + int n = s.length(); + + for (int i=1; i 0 && s[i] != s[j]) + j = pi[j-1]; + + if (s[i] == s[j]) + ++j; + + pi[i] = j; + } +} + +int main() { + std::string s, pattern; + std::cin >> pattern; + std::cin >> s; + s = pattern + '#' + s; + int n = s.length(); + + std::vector pi (n, 0); + prefix_function(s, pi); + + for (int i=2*pattern.length(); i +#include + +const int alphabet = 26; + +char new_character(std::string& s, std::vector& pi, int index) { + std::vector available_symbols(alphabet, true); + + while (index > 0) { + index = pi[index - 1]; + available_symbols[s[index] - 'a'] = false; + } + + for (int i = 0; i < alphabet; ++i) { + if (available_symbols[i]) { + return 'a' + i; + } + } + +} + +std::string buildFromPrefix(std::vector& pi) { + std::string s = ""; + for(int i = 0; i pi; + int tmp; + + while(std::cin >> tmp){ + pi.push_back(tmp); + } + + std::cout << buildFromPrefix(pi); + + return 0; +} diff --git a/2nd module/mod2_a.cpp b/2nd module/mod2_a.cpp new file mode 100644 index 0000000..a783556 --- /dev/null +++ b/2nd module/mod2_a.cpp @@ -0,0 +1,125 @@ +/* + * Дана строка длины n. Найти количество ее различных подстрок. Используйте суффиксный массив. + * Построение суффиксного массива выполняйте за O(n log n). + * Вычисление количества различных подстрок выполняйте за O(n). +*/ +#include +#include + + +void sufix_array(std::string& s, int alphabet, std::vector& suffs) { + int n = s.size(); + int cnt_len = (n > alphabet ? n : alphabet); + std::vector cnt(cnt_len, 0); + std::vector classes(n, 0); + + for (int i = 0; i < n; ++i) { // карманная сортировка подсчетом + ++cnt[s[i] - '\0']; + } + for (int i = 1; i < alphabet; ++i) // считаем границы + cnt[i] += cnt[i-1]; + // suffs будет хранить индексы начал отсортированных подстрок текущей длины + for (int i = 0; i < n; ++i) { + suffs[--cnt[s[i] - '\0']] = i; + } + + int class_numb = 0; + char last_char = '$'; + // каждому суффиксу длины 1 сопоставляем класс + for (int i = 0; i < n; ++i) { + if (s[suffs[i]] != last_char){ + last_char = s[suffs[i]]; + ++class_numb; + } + classes[suffs[i]] = class_numb; + } + + + + + // нулевая итерация завершена + // сортируем подстроки длиной 2 * cur_len = 2^k + int cur_len = 1; + std::vector sorted_by2(n, 0); + std::vector new_classes(n, 0); + while (cur_len <= n){ + // сортируем по второй половине подстроки + for (int i = 0; i < n; ++i){ + sorted_by2[i] = (suffs[i] + n - cur_len) % n; + } + // сортируем по первой половине + // сортировка устойчивая, значит получим целиком отсортированные подстроки + for (int i = 0; i < cnt_len; ++i){ // обнуляем cnt + cnt[i] = 0; + } + + for (int i = 0; i < n; ++i){ + ++cnt[classes[sorted_by2[i]]]; + } + + for (int i = 1; i < cnt_len; ++i) // считаем границы + cnt[i] += cnt[i-1]; + + + for (int i = n - 1; i >= 0; --i){ + suffs[--cnt[classes[sorted_by2[i]]]] = sorted_by2[i]; + } + + class_numb = 0; + for (int i = 1; i < n; ++i){ + int mid1 = (suffs[i] + cur_len) % n; + int mid2 = (suffs[i - 1] + cur_len) % n; + if (classes[suffs[i]] != classes[suffs[i - 1]] or classes[mid1] != classes[mid2]) + ++class_numb; + new_classes[suffs[i]] = class_numb; + } + for (int i = 0; i < n; ++i){ + classes[i] = new_classes[i]; + } + cur_len *= 2; + } +} + +void lcp_array(std::string& s, std::vector& suffs, std::vector& lcp){ + int n = s.length(); + int k = 0; + std::vector pos(n); + for(int i = 0; i < n; ++i) pos[suffs[i]] = i; // pos = suffs^-1 + + for(int i = 0; i < n; ++i){ + if (k > 0) --k; + if (pos[i] == n - 1){ + k = 0; + lcp[n - 1] = -1; + }else{ + int j = suffs[pos[i] + 1]; + while ((i + k > j + k ? i + k : j + k) < n and s[i + k] == s[j + k]) + ++k; + lcp[pos[i]] = k; + } + } +} + +int find_substrings(std::string& s){ + s += "$"; + + std::vector p(s.size(), 0); + std::vector lcp(s.size(), 0); + sufix_array(s, 256, p); + lcp_array(s, p, lcp); + + int ans = 0; + for (int i = 0; i < s.size(); ++i) { + //std::cout << p[i] << "|" << lcp[i] << " \n"; + 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/2nd module/mod2_c.cpp b/2nd module/mod2_c.cpp new file mode 100644 index 0000000..f02c1f5 --- /dev/null +++ b/2nd module/mod2_c.cpp @@ -0,0 +1,170 @@ +/* + * Заданы две строки s, t и целое число k. + * Рассмотрим множество всех таких непустых строк, которые встречаются как подстроки в s и t одновременно. + * Найдите k-ую в лексикографическом порядке строку из этого множества. + * Полезная статья про сравнение указателей: https://stackoverflow.com/questions/9086372/how-to-compare-pointers. +*/ +#include +#include + + +void sufix_array(std::string& s, int alphabet, std::vector& suffs) { + int n = s.size(); + int cnt_len = (n > alphabet ? n : alphabet); + std::vector cnt(cnt_len, 0); + std::vector classes(n, 0); + + for (int i = 0; i < n; ++i) { // карманная сортировка подсчетом + ++cnt[s[i] - '\0']; + } + for (int i = 1; i < alphabet; ++i) // считаем границы + cnt[i] += cnt[i-1]; + // suffs будет хранить индексы начал отсортированных подстрок текущей длины + for (int i = 0; i < n; ++i) { + suffs[--cnt[s[i] - '\0']] = i; + } + + int class_numb = 0; + char last_char = '$'; + // каждому суффиксу длины 1 сопоставляем класс + for (int i = 0; i < n; ++i) { + if (s[suffs[i]] != last_char){ + last_char = s[suffs[i]]; + ++class_numb; + } + classes[suffs[i]] = class_numb; + } + + + + + // нулевая итерация завершена + // сортируем подстроки длиной 2 * cur_len = 2^k + int cur_len = 1; + std::vector sorted_by2(n, 0); + std::vector new_classes(n, 0); + while (cur_len <= n){ + // сортируем по второй половине подстроки + for (int i = 0; i < n; ++i){ + sorted_by2[i] = (suffs[i] + n - cur_len) % n; + } + // сортируем по первой половине + // сортировка устойчивая, значит получим целиком отсортированные подстроки + for (int i = 0; i < cnt_len; ++i){ // обнуляем cnt + cnt[i] = 0; + } + + for (int i = 0; i < n; ++i){ + ++cnt[classes[sorted_by2[i]]]; + } + + for (int i = 1; i < cnt_len; ++i) // считаем границы + cnt[i] += cnt[i-1]; + + + for (int i = n - 1; i >= 0; --i){ + suffs[--cnt[classes[sorted_by2[i]]]] = sorted_by2[i]; + } + + class_numb = 0; + for (int i = 1; i < n; ++i){ + int mid1 = (suffs[i] + cur_len) % n; + int mid2 = (suffs[i - 1] + cur_len) % n; + if (classes[suffs[i]] != classes[suffs[i - 1]] or classes[mid1] != classes[mid2]) + ++class_numb; + new_classes[suffs[i]] = class_numb; + } + for (int i = 0; i < n; ++i){ + classes[i] = new_classes[i]; + } + cur_len *= 2; + } +} + +void lcp_array(std::string& s, std::vector& suffs, std::vector& lcp){ + int n = s.length(); + int k = 0; + std::vector pos(n); + for(int i = 0; i < n; ++i) pos[suffs[i]] = i; // pos = suffs^-1 + + for(int i = 0; i < n; ++i){ + if (k > 0) --k; + if (pos[i] == n - 1){ + k = 0; + lcp[n - 1] = -1; + }else{ + int j = suffs[pos[i] + 1]; + while ((i + k > j + k ? i + k : j + k) < n and s[i + k] == s[j + k]) + ++k; + lcp[pos[i]] = k; + } + } + + for(int i = n - 1; i > 0; --i){ + lcp[i] = lcp[i - 1]; + } + lcp[0] = -1; +} + +bool is_changed(std::vector& p, int i, int pivot){ + return (p[i] < pivot && p[i - 1] > pivot) || + (p[i] > pivot && p[i - 1] < pivot); +} + + +std::string find_k_substring(std::string& s, std::string& l, int64_t k){ + int old_s_size = s.size(); + s = s + "#" + l + "$"; + + std::vector p(s.size(), 0); + std::vector lcp(s.size(), 0); + sufix_array(s, 256, p); + lcp_array(s, p, lcp); + + for (int i = 0; i < s.size(); ++i) { + //std::cout << p[i] << "|" << lcp[i] << " \n"; + } + + int lcp_old = 0; + std::string ans; + + for (int i = 1; i < s.size(); ++i){ + if (is_changed(p, i, old_s_size)){ + //std::cout << p[i - 1] << " comp with " << p[i] << " " << "(" << lcp[i] << ") " << k << "\n"; + if (lcp_old + k > lcp[i]) { + if (lcp[i] >= lcp_old) { + k -= lcp[i] - lcp_old; + } + lcp_old = lcp[i]; + + }else{ + for (int 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; +} From 41bf355726a6925cb0d44e084ecf740007051a02 Mon Sep 17 00:00:00 2001 From: SweetWeakness Date: Tue, 19 Nov 2019 18:08:46 +0300 Subject: [PATCH 2/5] a problem (2nd module) --- 1st module/mod1_a.cpp | 49 -------- 1st module/mod1_b1.cpp | 45 -------- 2nd module/mod2_c.cpp | 170 ---------------------------- 2nd module/mod2_a.cpp => mod2_a.cpp | 0 4 files changed, 264 deletions(-) delete mode 100644 1st module/mod1_a.cpp delete mode 100644 1st module/mod1_b1.cpp delete mode 100644 2nd module/mod2_c.cpp rename 2nd module/mod2_a.cpp => mod2_a.cpp (100%) diff --git a/1st module/mod1_a.cpp b/1st module/mod1_a.cpp deleted file mode 100644 index 1c2dbeb..0000000 --- a/1st module/mod1_a.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include - - -void prefix_function (std::string& s, std::vector& pi) { - int n = s.length(); - - for (int i=1; i 0 && s[i] != s[j]) - j = pi[j-1]; - - if (s[i] == s[j]) - ++j; - - pi[i] = j; - } -} - -int main() { - std::string s, pattern; - std::cin >> pattern; - std::cin >> s; - s = pattern + '#' + s; - int n = s.length(); - - std::vector pi (n, 0); - prefix_function(s, pi); - - for (int i=2*pattern.length(); i -#include - -const int alphabet = 26; - -char new_character(std::string& s, std::vector& pi, int index) { - std::vector available_symbols(alphabet, true); - - while (index > 0) { - index = pi[index - 1]; - available_symbols[s[index] - 'a'] = false; - } - - for (int i = 0; i < alphabet; ++i) { - if (available_symbols[i]) { - return 'a' + i; - } - } - -} - -std::string buildFromPrefix(std::vector& pi) { - std::string s = ""; - for(int i = 0; i pi; - int tmp; - - while(std::cin >> tmp){ - pi.push_back(tmp); - } - - std::cout << buildFromPrefix(pi); - - return 0; -} diff --git a/2nd module/mod2_c.cpp b/2nd module/mod2_c.cpp deleted file mode 100644 index f02c1f5..0000000 --- a/2nd module/mod2_c.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Заданы две строки s, t и целое число k. - * Рассмотрим множество всех таких непустых строк, которые встречаются как подстроки в s и t одновременно. - * Найдите k-ую в лексикографическом порядке строку из этого множества. - * Полезная статья про сравнение указателей: https://stackoverflow.com/questions/9086372/how-to-compare-pointers. -*/ -#include -#include - - -void sufix_array(std::string& s, int alphabet, std::vector& suffs) { - int n = s.size(); - int cnt_len = (n > alphabet ? n : alphabet); - std::vector cnt(cnt_len, 0); - std::vector classes(n, 0); - - for (int i = 0; i < n; ++i) { // карманная сортировка подсчетом - ++cnt[s[i] - '\0']; - } - for (int i = 1; i < alphabet; ++i) // считаем границы - cnt[i] += cnt[i-1]; - // suffs будет хранить индексы начал отсортированных подстрок текущей длины - for (int i = 0; i < n; ++i) { - suffs[--cnt[s[i] - '\0']] = i; - } - - int class_numb = 0; - char last_char = '$'; - // каждому суффиксу длины 1 сопоставляем класс - for (int i = 0; i < n; ++i) { - if (s[suffs[i]] != last_char){ - last_char = s[suffs[i]]; - ++class_numb; - } - classes[suffs[i]] = class_numb; - } - - - - - // нулевая итерация завершена - // сортируем подстроки длиной 2 * cur_len = 2^k - int cur_len = 1; - std::vector sorted_by2(n, 0); - std::vector new_classes(n, 0); - while (cur_len <= n){ - // сортируем по второй половине подстроки - for (int i = 0; i < n; ++i){ - sorted_by2[i] = (suffs[i] + n - cur_len) % n; - } - // сортируем по первой половине - // сортировка устойчивая, значит получим целиком отсортированные подстроки - for (int i = 0; i < cnt_len; ++i){ // обнуляем cnt - cnt[i] = 0; - } - - for (int i = 0; i < n; ++i){ - ++cnt[classes[sorted_by2[i]]]; - } - - for (int i = 1; i < cnt_len; ++i) // считаем границы - cnt[i] += cnt[i-1]; - - - for (int i = n - 1; i >= 0; --i){ - suffs[--cnt[classes[sorted_by2[i]]]] = sorted_by2[i]; - } - - class_numb = 0; - for (int i = 1; i < n; ++i){ - int mid1 = (suffs[i] + cur_len) % n; - int mid2 = (suffs[i - 1] + cur_len) % n; - if (classes[suffs[i]] != classes[suffs[i - 1]] or classes[mid1] != classes[mid2]) - ++class_numb; - new_classes[suffs[i]] = class_numb; - } - for (int i = 0; i < n; ++i){ - classes[i] = new_classes[i]; - } - cur_len *= 2; - } -} - -void lcp_array(std::string& s, std::vector& suffs, std::vector& lcp){ - int n = s.length(); - int k = 0; - std::vector pos(n); - for(int i = 0; i < n; ++i) pos[suffs[i]] = i; // pos = suffs^-1 - - for(int i = 0; i < n; ++i){ - if (k > 0) --k; - if (pos[i] == n - 1){ - k = 0; - lcp[n - 1] = -1; - }else{ - int j = suffs[pos[i] + 1]; - while ((i + k > j + k ? i + k : j + k) < n and s[i + k] == s[j + k]) - ++k; - lcp[pos[i]] = k; - } - } - - for(int i = n - 1; i > 0; --i){ - lcp[i] = lcp[i - 1]; - } - lcp[0] = -1; -} - -bool is_changed(std::vector& p, int i, int pivot){ - return (p[i] < pivot && p[i - 1] > pivot) || - (p[i] > pivot && p[i - 1] < pivot); -} - - -std::string find_k_substring(std::string& s, std::string& l, int64_t k){ - int old_s_size = s.size(); - s = s + "#" + l + "$"; - - std::vector p(s.size(), 0); - std::vector lcp(s.size(), 0); - sufix_array(s, 256, p); - lcp_array(s, p, lcp); - - for (int i = 0; i < s.size(); ++i) { - //std::cout << p[i] << "|" << lcp[i] << " \n"; - } - - int lcp_old = 0; - std::string ans; - - for (int i = 1; i < s.size(); ++i){ - if (is_changed(p, i, old_s_size)){ - //std::cout << p[i - 1] << " comp with " << p[i] << " " << "(" << lcp[i] << ") " << k << "\n"; - if (lcp_old + k > lcp[i]) { - if (lcp[i] >= lcp_old) { - k -= lcp[i] - lcp_old; - } - lcp_old = lcp[i]; - - }else{ - for (int 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/2nd module/mod2_a.cpp b/mod2_a.cpp similarity index 100% rename from 2nd module/mod2_a.cpp rename to mod2_a.cpp From 3b125c19d6dc9c8e3874cad4bf84101bed6f7e78 Mon Sep 17 00:00:00 2001 From: SweetWeakness Date: Tue, 19 Nov 2019 23:19:35 +0300 Subject: [PATCH 3/5] a remake (2nd module) --- .gitignore | 3 +- mod2_a.cpp | 109 +++++++++++++++++++++++++++++++++++------------------ 2 files changed, 75 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index 723ef36..6c953ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.idea \ No newline at end of file +.idea +CMakeLists.txt \ No newline at end of file diff --git a/mod2_a.cpp b/mod2_a.cpp index a783556..99d7a3a 100644 --- a/mod2_a.cpp +++ b/mod2_a.cpp @@ -3,28 +3,42 @@ * Построение суффиксного массива выполняйте за O(n log n). * Вычисление количества различных подстрок выполняйте за O(n). */ + + #include #include -void sufix_array(std::string& s, int alphabet, std::vector& suffs) { - int n = s.size(); - int cnt_len = (n > alphabet ? n : alphabet); - std::vector cnt(cnt_len, 0); - std::vector classes(n, 0); +// сортируем посимвольно строку +std::vector zero_iteration (const std::string &s, const int alphabet) { + const int n = s.length(); + std::vector suffs (n, 0); // for return + std::vector cnt (alphabet, 0); - for (int i = 0; i < n; ++i) { // карманная сортировка подсчетом + // карманная сортировка подсчетом + for (int i = 0; i < n; ++i) { ++cnt[s[i] - '\0']; } - for (int i = 1; i < alphabet; ++i) // считаем границы - cnt[i] += cnt[i-1]; + // считаем границы + for (int i = 1; i < alphabet; ++i) { + cnt[i] += cnt[i - 1]; + } // suffs будет хранить индексы начал отсортированных подстрок текущей длины for (int i = 0; i < n; ++i) { suffs[--cnt[s[i] - '\0']] = i; } + return suffs; +} + + +// создаем классы для последующих итераций +std::vector make_classes (const std::string& s, const std::vector& suffs) { + const int n = s.length(); + std::vector classes (n, 0); // for return int class_numb = 0; char last_char = '$'; + // каждому суффиксу длины 1 сопоставляем класс for (int i = 0; i < n; ++i) { if (s[suffs[i]] != last_char){ @@ -34,22 +48,27 @@ void sufix_array(std::string& s, int alphabet, std::vector& suffs) { classes[suffs[i]] = class_numb; } + return classes; +} + +// сортируем циклические сдвиги строки длины степеней двойки (2, 4, 8, ... n) +void main_iterations (std::vector& suffs, std::vector& classes) { + const int n = suffs.size(); + std::vector cnt (n, 0); + std::vector sorted_by2 (n, 0); + std::vector new_classes (n, 0); - // нулевая итерация завершена - // сортируем подстроки длиной 2 * cur_len = 2^k - int cur_len = 1; - std::vector sorted_by2(n, 0); - std::vector new_classes(n, 0); - while (cur_len <= n){ + for (int cur_len = 1; cur_len <= n; cur_len *= 2){ // сортируем по второй половине подстроки for (int i = 0; i < n; ++i){ sorted_by2[i] = (suffs[i] + n - cur_len) % n; } // сортируем по первой половине // сортировка устойчивая, значит получим целиком отсортированные подстроки - for (int i = 0; i < cnt_len; ++i){ // обнуляем cnt + // обнуляем cnt + for (int i = 0; i < n; ++i){ cnt[i] = 0; } @@ -57,62 +76,79 @@ void sufix_array(std::string& s, int alphabet, std::vector& suffs) { ++cnt[classes[sorted_by2[i]]]; } - for (int i = 1; i < cnt_len; ++i) // считаем границы - cnt[i] += cnt[i-1]; - + for (int i = 1; i < n; ++i) {// считаем границы + cnt[i] += cnt[i - 1]; + } for (int i = n - 1; i >= 0; --i){ suffs[--cnt[classes[sorted_by2[i]]]] = sorted_by2[i]; } - class_numb = 0; + //подсчтываем классы заново + int class_numb = 0; for (int i = 1; i < n; ++i){ int mid1 = (suffs[i] + cur_len) % n; int mid2 = (suffs[i - 1] + cur_len) % n; - if (classes[suffs[i]] != classes[suffs[i - 1]] or classes[mid1] != classes[mid2]) + if (classes[suffs[i]] != classes[suffs[i - 1]] || classes[mid1] != classes[mid2]) { ++class_numb; + } new_classes[suffs[i]] = class_numb; } - for (int i = 0; i < n; ++i){ - classes[i] = new_classes[i]; - } - cur_len *= 2; + + classes = new_classes; } } -void lcp_array(std::string& s, std::vector& suffs, std::vector& lcp){ - int n = s.length(); + +std::vector make_suff_array (const std::string& s, const int alphabet) { + std::vector suffs = zero_iteration(s, alphabet); + std::vector classes = make_classes(s, suffs); + main_iterations(suffs, classes); + return suffs; +} + + +std::vector make_lcp_array(const std::string& s, const std::vector& suffs){ + const int n = s.length(); + std::vector lcp (n, 0); int k = 0; std::vector pos(n); - for(int i = 0; i < n; ++i) pos[suffs[i]] = i; // pos = suffs^-1 + for(int i = 0; i < n; ++i) { + pos[suffs[i]] = i; // pos = suffs^-1 + } for(int i = 0; i < n; ++i){ - if (k > 0) --k; - if (pos[i] == n - 1){ + if (k > 0) { + --k; + } + if (pos[i] == n - 1) { k = 0; lcp[n - 1] = -1; - }else{ + } else { int j = suffs[pos[i] + 1]; - while ((i + k > j + k ? i + k : j + k) < n and s[i + k] == s[j + k]) + while ((i + k > j + k ? i + k : j + k) < n and s[i + k] == s[j + k]) { ++k; + } lcp[pos[i]] = k; } } + + return lcp; } int find_substrings(std::string& s){ s += "$"; + std::vector p; + std::vector lcp; - std::vector p(s.size(), 0); - std::vector lcp(s.size(), 0); - sufix_array(s, 256, p); - lcp_array(s, p, lcp); + p = make_suff_array(s, 256); + lcp = make_lcp_array(s, p); int ans = 0; for (int i = 0; i < s.size(); ++i) { - //std::cout << p[i] << "|" << lcp[i] << " \n"; ans += s.size() - 1 - p[i] - lcp[i]; } + return --ans; } @@ -121,5 +157,6 @@ int main() { std::cin >> s; std::cout << find_substrings(s); + return 0; } From 211eed2c979b790c33747fba13ab094cda313a73 Mon Sep 17 00:00:00 2001 From: SweetWeakness Date: Sun, 24 Nov 2019 09:12:01 +0300 Subject: [PATCH 4/5] remade --- mod2_a.cpp | 105 +++++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/mod2_a.cpp b/mod2_a.cpp index 99d7a3a..c3e34a6 100644 --- a/mod2_a.cpp +++ b/mod2_a.cpp @@ -9,22 +9,24 @@ #include +constexpr size_t ALPHABET = 256; + + // сортируем посимвольно строку -std::vector zero_iteration (const std::string &s, const int alphabet) { - const int n = s.length(); - std::vector suffs (n, 0); // for return - std::vector cnt (alphabet, 0); +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 (int i = 0; i < n; ++i) { + for (size_t i = 0; i < s.length(); ++i) { ++cnt[s[i] - '\0']; } // считаем границы - for (int i = 1; i < alphabet; ++i) { + for (size_t i = 1; i < alphabet; ++i) { cnt[i] += cnt[i - 1]; } // suffs будет хранить индексы начал отсортированных подстрок текущей длины - for (int i = 0; i < n; ++i) { + for (size_t i = 0; i < s.length(); ++i) { suffs[--cnt[s[i] - '\0']] = i; } @@ -33,14 +35,13 @@ std::vector zero_iteration (const std::string &s, const int alphabet) { // создаем классы для последующих итераций -std::vector make_classes (const std::string& s, const std::vector& suffs) { - const int n = s.length(); - std::vector classes (n, 0); // for return - int class_numb = 0; +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 (int i = 0; i < n; ++i) { + for (size_t i = 0; i < s.length(); ++i) { if (s[suffs[i]] != last_char){ last_char = s[suffs[i]]; ++class_numb; @@ -53,42 +54,42 @@ std::vector make_classes (const std::string& s, const std::vector& suf // сортируем циклические сдвиги строки длины степеней двойки (2, 4, 8, ... n) -void main_iterations (std::vector& suffs, std::vector& classes) { - const int n = suffs.size(); - std::vector cnt (n, 0); - std::vector sorted_by2 (n, 0); - std::vector new_classes (n, 0); +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 (int cur_len = 1; cur_len <= n; cur_len *= 2){ + for (size_t cur_len = 1; cur_len <= s_length; cur_len *= 2) { // сортируем по второй половине подстроки - for (int i = 0; i < n; ++i){ - sorted_by2[i] = (suffs[i] + n - cur_len) % n; + for (size_t i = 0; i < s_length; ++i){ + sorted_by2[i] = (suffs[i] + s_length - cur_len) % s_length; } // сортируем по первой половине // сортировка устойчивая, значит получим целиком отсортированные подстроки // обнуляем cnt - for (int i = 0; i < n; ++i){ + for (size_t i = 0; i < s_length; ++i){ cnt[i] = 0; } - for (int i = 0; i < n; ++i){ + for (size_t i = 0; i < s_length; ++i){ ++cnt[classes[sorted_by2[i]]]; } - for (int i = 1; i < n; ++i) {// считаем границы + for (size_t i = 1; i < s_length; ++i) {// считаем границы cnt[i] += cnt[i - 1]; } - for (int i = n - 1; i >= 0; --i){ + for (int i = s_length - 1; i >= 0; --i){ suffs[--cnt[classes[sorted_by2[i]]]] = sorted_by2[i]; } //подсчтываем классы заново - int class_numb = 0; - for (int i = 1; i < n; ++i){ - int mid1 = (suffs[i] + cur_len) % n; - int mid2 = (suffs[i - 1] + cur_len) % n; + 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; } @@ -100,52 +101,52 @@ void main_iterations (std::vector& suffs, std::vector& classes) { } -std::vector make_suff_array (const std::string& s, const int alphabet) { - std::vector suffs = zero_iteration(s, alphabet); - std::vector classes = make_classes(s, suffs); +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; } -std::vector make_lcp_array(const std::string& s, const std::vector& suffs){ - const int n = s.length(); - std::vector lcp (n, 0); - int k = 0; - std::vector pos(n); - for(int i = 0; i < n; ++i) { +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(int i = 0; i < n; ++i){ - if (k > 0) { - --k; + for (size_t i = 0; i < s.length(); ++i) { + if (cur_lcp > 0) { + --cur_lcp; } - if (pos[i] == n - 1) { - k = 0; - lcp[n - 1] = -1; + if (pos[i] == s.length() - 1) { + cur_lcp = 0; + lcp[s.length() - 1] = -1; } else { - int j = suffs[pos[i] + 1]; - while ((i + k > j + k ? i + k : j + k) < n and s[i + k] == s[j + k]) { - ++k; + 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]] = k; + lcp[pos[i]] = cur_lcp; } } return lcp; } -int find_substrings(std::string& s){ +size_t find_substrings(std::string& s){ s += "$"; - std::vector p; + std::vector p; std::vector lcp; - p = make_suff_array(s, 256); + p = make_suff_array(s); lcp = make_lcp_array(s, p); - int ans = 0; - for (int i = 0; i < s.size(); ++i) { + size_t ans = 0; + for (size_t i = 0; i < s.size(); ++i) { ans += s.size() - 1 - p[i] - lcp[i]; } From 36933cab993ecd4a7d8599106d7799ac63ca860a Mon Sep 17 00:00:00 2001 From: SweetWeakness Date: Sun, 8 Dec 2019 00:46:05 +0300 Subject: [PATCH 5/5] =?UTF-8?q?=D0=B4=D0=BE=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lcp_array.h | 34 +++++++++++++ mod2_a.cpp | 127 +------------------------------------------------ suffix_array.h | 100 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 125 deletions(-) create mode 100644 lcp_array.h create mode 100644 suffix_array.h 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 index c3e34a6..3fe5c06 100644 --- a/mod2_a.cpp +++ b/mod2_a.cpp @@ -7,136 +7,13 @@ #include #include +#include +#include constexpr size_t ALPHABET = 256; -// сортируем посимвольно строку -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; -} - - -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; -} - size_t find_substrings(std::string& s){ s += "$"; std::vector p; 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