Форум программистов, компьютерный форум, киберфорум
Алгоритмы
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.91/11: Рейтинг темы: голосов - 11, средняя оценка - 4.91
1 / 1 / 0
Регистрация: 01.11.2019
Сообщений: 31

Проблема в оптимизации алгоритма (Нахождение подстрок в строке, работа с большими данными)

04.11.2023, 22:32. Показов 2563. Ответов 30

Студворк — интернет-сервис помощи студентам
Решаю задачу и не могу сдвинуться с места. Подскажите пожалуйста, куда думать.
В общем задача:
У меня есть строка S длиной n. Я должен сформировать подстроку R из k последних элементов этой строки и найти наиболее часто встречающейся элемент в строке S, который идет после подстроки R. Полученный элемент я добавляю в конец строки S. Такие действия я должен проделать очень много раз. Если после подстроки нету символов, то добавляется 'a'.

Ограничения:
S - строка из букв английского алфавита('a'-'z').
k<n<a<b,
b<10^18,
2<=n<=10^6,
1<=k<n,
b-a+1<=10^6

В итоге я должен вывести элементы увеличенной строки S начиная с a-тей позиции до b-тей позиции включительно, то есть элементов будет ровно b-a+1.

Например, для входных данных(n, k, a, b):
6 1 7 10
qwertq

Ответом будет: wert. Строка после действия:1) qwertqw, 2)qwertqwe, 3)qwertqwer, 4) qwertqwert.

У меня пока что идея такая: Использовать алгоритм Кнута-Морриса-Пратта для поиска подстрок из k-последних символов начальной строки. Найти наиболее встречающейся символ и добавить его в конец изначальной строки S. И так проделать от
n до b. Но b может быть слишком большое(10^18) и если b хотя бы 10^8, уже проблемы.
Сегодня утром пришла идея: провести 52 операции с добавлением новых символов в конец строки S, потом найти образец повторяющейся подстроки в строке S, т.к. случае с большим b, все, что идет после[S.size()] в строке S по идее будет зациклено этим образцом. Но что дальше делать с этим образцом, я уже без понятия. Пробовал как-то работать с остатком от деления по индексам, но все равно нахожу случаи с неверным ответом. Куда думать в этой задаче, я уже без понятия.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
04.11.2023, 22:32
Ответы с готовыми решениями:

Нахождение подстрок в строке
Доброго времени суток. Столкнулся с трудностями нахождения ключа для зашифрованного текста. Ключ должен находится самостоятельно путем...

Работа с большими данными
Не знаю к какому разделу относится данный вопрос, решил написать сюда. Есть сайт, у него большая база данных. Из доступов только ftp и...

Работа с большими данными
Здравствуйте. Так получилось, что сейчас надо написать сайт с большим количеством контента (таблица с контентом &gt; 1GB). И я хочу что-бы...

30
1968 / 824 / 115
Регистрация: 01.10.2012
Сообщений: 4,868
Записей в блоге: 2
05.11.2023, 15:50
Цитата Сообщение от Владислав2313 Посмотреть сообщение
У меня есть строка S длиной n. Я должен сформировать подстроку R из k последних элементов этой строки и найти наиболее часто встречающейся элемент в строке S, который идет после подстроки R. Полученный элемент я добавляю в конец строки S. Такие действия я должен проделать очень много раз. Если после подстроки нету символов, то добавляется 'a'.
Неясно что здесь значит "сформировать". Неясно с "много раз" - со многими строками, что ли?

Ладно, пытаюсь читать дальше. Но приводимый Вами пример еще более непонятен. И начинать (мучительные) выяснения в чем собсно задача - никакого желания нет. Подумайте как сформулировать задачу чтобы она (возможно) вылилась в интересное/плодотворное обсуждение, а не отягощала форум
0
1 / 1 / 0
Регистрация: 01.11.2019
Сообщений: 31
05.11.2023, 17:28  [ТС]
Полная формулировка задания.

Вася обнаружил в себе страсть к машинному обучению. В настоящее время он работает над проектом новой языковой модели, которую он назвал ChatGPT. Модель на входе получает n-буквенное слово S и параметр k (целое число 1 ≤ k < n), а затем генерирует продолжение слова. Предположим , у нас уже есть слово S', которое является расширением S некоторыми буквами. Добавление новой буквы будет выглядеть следующим образом (см. Также пример ниже): мы рассматриваем K-буквенный суффикс R слова S 'и смотрим на все предыдущие вхождения R в слове S' (как последовательное подслово). Затем для каждой буквы из алфавита мы подсчитываем, сколько раз она появлялась непосредственно после R в слове S'. Пусть c-буква, которая встречается чаще всего. Мы решаем ничьи в пользу буквы, ранее встречавшейся в алфавите , и если R не встречается где-либо еще в слове S', Мы имеем c = 'a'. в конце мы расширяем слово S', добавляя в его конце букву c. например, пусть S = abaaabababa и k = 3. У нас есть S ' = S, R = aba и R встречается раньше со следующей буквой как: abaa, abab, abab. Чаще всего встречается с буквой b, поэтому к S ' мы добавляем b. Теперь S '= abaaabababab, R = bab а также R встречается раньше со следующей буквой как baba, baba, поэтому мы добавляем к S' 'a'. Ваша задача-написать программу, которая будет реализовывать модель, разработанную Василием.

Вход
В первой строке ввода четыре целых числа n, k, a и b (2 ≤ n ≤ 10^6 , 1 ≤ k < n, n < a < b < 10^18 , b + 1 − a ≤ 10^6 ). Во второй строке ввода находится n-буквенная строка, состоящая из строчных букв английского алфавита ('a' - 'z'), обозначающая слово S.

Выход
На вывод следует вывести строку b+1-a, обозначающую буквы в расширенном слове S' в позициях от а-й до b-й (включительно). Другими словами, мы предполагаем, что к исходному слову S было добавлено b-n букв, и мы хотим вывести последние b+1-a из этих добавленных букв.

Пример
Для ввода:
11 3 12 13
abaaabababa

Ответ: ba

Как пытаюсь решать я, но почему-то не выходит. Определенное кол-во раз выполняю алгоритм КМП для поиска всех вхождений подстроки в строку, нахожу самый часто встречающейся элемент в данной строке после подстроки, добавляю
этот элемент в строку и так 1000 раз. После, беру последний элемент строки и нахожу зацикливание как-то так:
C++
1
2
3
4
5
if(s[i]==s[j] && s[i-1]==s[j-1] && s[i-2]==s[j-2] && s[i-3]==s[j-3] && s[i-4]==s[j-4])
{
    start_otl=i-j;
    jt = i-start_otl;
}
где jt - нулевой элемент циклической подстроки, start_otl - кол-во элементов в этой подстроке.
Потом вычисляю по формуле, сколько мне нужно прибавить к индексу нулевого элемента(jt), чтобы получить элемент с позицией a-1.

количество_элем*i + индекс_нулевого_элем + количество_элем = a. => i=(a-jt-start_otl)/start_otl.
где i - перебирается циклически с 0 до start_otl.

По идее должно работать, но при больших n(около 10^5*2) и с множеством разных букв, выполняется долго(30 сек.) и иногда выводит неправильный ответ.

Добавлено через 20 минут
Igor3D, Сформировать, то есть добавлять новые элементы в исходную строку.
Пример:
6 1 7 10
qwertq

Я из этой строки беру суфикс размером с 1, тоесть 'q'. После, нахожу наиболее встречающейся символ в исходной строке после суфикса q, то есть 'w' и его добавляю в конец исходной строки. Получаю qwertqw и снова беру суфикс размером с 1, то есть 'w'. Делаю тоже самое, поучаю элемент 'e' и добавляю в конец строки. Получаю qwertqwe и так далее до позиции b-1.
В итоге вывожу подстроку с элементами начиная од индекса a-1, заканчивая индексом b-1 включительно.
Ответ: wert.
0
 Аватар для igorrr37
2878 / 2025 / 991
Регистрация: 21.12.2010
Сообщений: 3,763
Записей в блоге: 9
06.11.2023, 00:02
Идея оптимизировать: для любой подстроки самый частый символ идущий после неё так и останется самым частым так как именно он и будет добавляться после неё, поэтому не надо искать его каждый раз, достаточно найти его один раз и запомнить. Работать прога должна быстро. Что касается потребления памяти то при небольших k различных подстрок будет немного так что памяти займёт немного. А вот при больших k - вопрос.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>
#include <climits>
#include <string_view>
#include <unordered_map>
#include <functional>
 
 
int main()
{
    std::string str{ "abaaabababa" };
    int n = str.size(), k = 3, a = 12, b = 13;
    std::unordered_map<size_t, std::pair<char, std::vector<int>>> umpPrep; // готовые подстроки длиной k(не сами подстроки а их хеш) и самый частый символ
 
    // в цикле делим строку str на подстроки. Каждую подстроку длиной k кидаем в ump вместе с самым частым символом
    for (int i = 0; i + k < str.size(); ++i)
    {
        auto h = std::hash<std::string_view>{}({ &str[i], (size_t)k });
        auto pr = umpPrep.emplace(h, std::pair<char, std::vector<int>>{ CHAR_MAX, std::vector<int>(26) }); // хеш подстроки
        auto it = pr.first;
        auto& v = it->second.second;
        auto& cmax = it->second.first;
        char c = str[i + k]; // символ
        ++v[c - 'a'];
        if (pr.second || (v[c - 'a'] > v[cmax] || (v[c - 'a'] == v[cmax] && c - 'a' < cmax)))
        {
            cmax = c - 'a'; // обновляем инфу о самом частом символе
        }
    }
 
    std::unordered_map<size_t, char> ump; // готовые подстроки длиной k(не сами подстроки а их хеш) и самый частый символ
    for (auto const& [k, v] : umpPrep)
    {
        ump.emplace(k, v.first + 'a');
    }
    umpPrep.clear();
 
    // в цикле добавляем в конец строки str  b-n букв
    for (int i = 0; i < b - n; ++i)
    {
        auto h = std::hash<std::string_view>{}({ str.end() - k, str.end()});
        auto pr = ump.emplace(h, 'a');
        auto it = pr.first;
        char c = it->second;
        str += c;
    }
    std::cout << std::string_view{str.end() - (b-a+1), str.end()} << "\n";
    int yy = 0;
}
1
1968 / 824 / 115
Регистрация: 01.10.2012
Сообщений: 4,868
Записей в блоге: 2
06.11.2023, 14:27

Не по теме:

Цитата Сообщение от Владислав2313 Посмотреть сообщение
Полная формулировка задания.
Ну вот, это другое дело. Правда итоговая цель мне непонятна, но это и не нужно.

Ну что, нужно читать/учить теорию, она здесь есть. Открыл наугад ссылку, ну сходу все понять конечно не удалось. Разумеется речь идет о солидных строках размером с мульен, для просто алфавита пройдет все что угодно. Реализация на С/С++, без злоупотреблений std

Конечно это не тот ответ что "сильно поможет", но другого здесь может и не быть
0
1 / 1 / 0
Регистрация: 01.11.2019
Сообщений: 31
06.11.2023, 21:15  [ТС]
Igor3D, Здесь прикол в том, что я изначально использовал алгоритм Кнута-Морриса-Пратта за время O(N+M), вроде бы ок, но если входная строка 10^6 и очень замудренная(например огромный, логичный текст), то непонятно за сколько таких проходов КМП я узнаю первый индекс цикличной подстроки(которая в дальнейшем будет повторятся). На сколько я понимаю, составители этой задачи хотят не больше O(200(N+M)), а как это оптимизировать, я без понятия.

Добавлено через 9 минут
igorrr37, Не совсем понял. По идее мы должны всегда искать самый частый символ, т.к. строка обновляется и собственно подстрока тоже.
Например:
6 1 7 10
qwertq

Подстрока 'q' с индексом 5. Ищем ее в строке 'qwertq', нашли на позиции 0, после нее идет 'w' и нашли на позиции 5, после нее нет ничего. Наиболее встречающейся символ 'w' добавляем в конец, получаем строку 'qwertqw'. Опять берем подстроку с конца, размером 1 - это 'w'. Проделываем то же самое, находим самый частый символ - 'e' и добавляем в конец. Такие действия нужно проделать до b-1, то есть увеличить строку до девятого индекса(с размером =b=10). b - может быть 10^18-1.

Добавлено через 6 минут
Цитата Сообщение от Igor3D Посмотреть сообщение
для просто алфавита пройдет все что угодно
По моей схеме (префикс функция + Кнут-Моррис-Пратт + нахождение циклической подстроки) в тестах с огромными данными, отрабатывает за 35-40, а иногда и за 60 секунд и не всегда правильно из-за нахождения неверной циклической подстроки. В задаче хотят 5 с.
0
1968 / 824 / 115
Регистрация: 01.10.2012
Сообщений: 4,868
Записей в блоге: 2
07.11.2023, 05:55
Похоже из алгоритмов "быстрого поиска" ничего не выжать. Так или иначе они прожевывают всю (потенциально огромную) строку, поэтому выигрыш (по сравнению с прямым поиском) не так уж велик. Нужно делать упор на то что (последовательно) ищутся подобные/близкие строки, и результаты предыдущего поиска могут быть использованы для последующего. Есть простой способ это сделать. Пример

Пусть искомая подстрока "abcd". В исходной строке найдем позиции всех "cd" (вторя половина искомой). Это будет относительно небольшой вектор int. Просматривая его можно найти все подстроки "abcd", "bcd?", "cd??" где ? - любой новый добавленный символ. Правда когда вытеснено "c" - ничего не попишешь, придется опять месить всю строку собирая новый вектор. Ну и надо "подсматривать" стартовые и/или конечные буквы в исходной строке, читается лишь ее небольшая часть но "вразнобой", сбивается кеш. Для искомой длиной 1 или 2 это не годится, нужен прямой поиск. Для длин 3 или 4 вероятно эффект невелик или отрицательный (расходы на создание вектора). А вот с увеличением длины искомой КПД/эффективность быстро растет.

Реализация несложна, но требует "прямизны рук"
1
698 / 574 / 75
Регистрация: 20.09.2014
Сообщений: 3,710
07.11.2023, 06:35
a = n + 1,
b = n + 1 + m,
где m - это число символов, которые надо добавить в строку S.
Зачем усложнять задачу этими a и b? Из-за этого и есть путаница во всем.

Если алгоритм не работает правильно, значит он неправильно реализован.
0
 Аватар для igorrr37
2878 / 2025 / 991
Регистрация: 21.12.2010
Сообщений: 3,763
Записей в блоге: 9
07.11.2023, 10:01
Цитата Сообщение от Владислав2313 Посмотреть сообщение
мы должны всегда искать самый частый символ
Я имел ввиду что после подстроки q самым частым всегда будет символ w, можно это запомнить и больше не искать подстроку q в строке. Здесь также надо делать скользящий хэш так как окно сдвигается каждый раз на 1 символ вправо. То есть в моём коде надо заменить std::hash с линейной сложностью O(k) на скользящий хеш с константной сложностью потому что при больших k std::hash долго считает
0
1968 / 824 / 115
Регистрация: 01.10.2012
Сообщений: 4,868
Записей в блоге: 2
07.11.2023, 15:54
Цитата Сообщение от igorrr37 Посмотреть сообщение
Я имел ввиду что после подстроки q самым частым всегда будет символ w
Интересная идея, верю что для 'a'-'z' прокатит. А вот для юникода (а по-хорошему надо делать в нем) думаю захлебнется
0
Модератор
Эксперт функциональных языков программирования
3134 / 2281 / 469
Регистрация: 26.03.2015
Сообщений: 8,878
08.11.2023, 21:39
В данной задаче поиск подстроки вообще не нужен. Один раз проходим по строке, подсчитывая частоту символов после каждой подстроки заданной длины. Затем частоту символов заменяем на самый частый. По этой таблице (подстрока - символ) можно построить всю строку до нужной позиции.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
string Solve(Dictionary<string, char> dic, StringBuilder sb, int len, int from, int upto)
{
    for(int i = sb.Length - len; i < upto; i++)
    {
        var s = sb.ToString(i, len);
        if (!dic.TryGetValue(s, out char c))
        {
            c = 'a';
        }
        sb.Append(c);
    }
    sb.ToString().Dump();
    return sb.ToString(from - 1, upto - from + 1);
}
 
Dictionary<string, char> CreateDictionary(string str, int len)
{
    var d1 = new Dictionary<string, Dictionary<char, int>>();
    for(int i = 0; i < str.Length - len; i++)
    {
        var s = str.Substring(i, len);
        var c = str[i + len];
        if (d1.TryGetValue(s, out Dictionary<char, int> d2))
        {
            if (d2.TryGetValue(c, out int cnt))
            {
                d2[c] = cnt + 1;            
            }
            else
            {
                d2.Add(c, 1);
            }
        }
        else
        {
            d1.Add(s, new Dictionary<char, int> { { c, 1} });
        }
    }
 
    return d1.Select(d => new { d.Key, Value = d.Value.Where(x => x.Value == d.Value.Max(y => y.Value)).First().Key })
        .ToDictionary(x => x.Key, x => x.Value);
}
Но это, что называется, решение "в лоб". Длина строки 106, а позиция 1018. Очевидно, что строка, которую мы строим, зацикливается (начиная с какой-то позиции). Нужно думать в сторону вычисления параметров этого цикла и выдачи ответа без построения строки. Проще всего добавить в код проверку, а не зациклились ли мы.
1
1968 / 824 / 115
Регистрация: 01.10.2012
Сообщений: 4,868
Записей в блоге: 2
09.11.2023, 13:02
Цитата Сообщение от Shamil1 Посмотреть сообщение
Один раз проходим по строке, подсчитывая частоту символов после каждой подстроки заданной длины.
Насколько я понял - вариация той же идеи. На всякий случай прожуем

Вот есть строка "абвгдеж..", ищется какая-то подстрока длиной 4. Помещаем "абвг" в хеш/словарь, потом "бвгд", "вгде" и.т.д., в итоге имеем словарь "на все случаи жизни".

Если так то: словарь может оказаться слишком велик. Да и вызов Substring и TryGetValue для такой кратности - не верю
0
698 / 574 / 75
Регистрация: 20.09.2014
Сообщений: 3,710
09.11.2023, 18:31
Если у нас начало слова "alph", то последующие буквы можно сразу выдать пачкой "a" или "abet", т.е. слово целиком - "alpha" или "alphabet". Каждая буква таких слов будет частая.

Добавлено через 1 минуту
Но я задачу нифига не понимаю. Число k фиксированное или изменяется в процессе?
0
Модератор
Эксперт функциональных языков программирования
3134 / 2281 / 469
Регистрация: 26.03.2015
Сообщений: 8,878
10.11.2023, 00:00
Цитата Сообщение от Igor3D Посмотреть сообщение
Насколько я понял - вариация той же идеи.
Да. Строится тот же словарь, но не постепенно (по требованию = мемоизация), а сразу. При этом он может оказаться избыточным, зато без поиска подстрок.

Цитата Сообщение от Igor3D Посмотреть сообщение
Помещаем "абвг" в хеш/словарь, потом "бвгд", "вгде" и.т.д., в итоге имеем словарь "на все случаи жизни".
Да.

Цитата Сообщение от Igor3D Посмотреть сообщение
Если так то: словарь может оказаться слишком велик.
Да.

Цитата Сообщение от Igor3D Посмотреть сообщение
Да и вызов Substring и TryGetValue для такой кратности - не верю
Не понял.
0
1968 / 824 / 115
Регистрация: 01.10.2012
Сообщений: 4,868
Записей в блоге: 2
10.11.2023, 12:34
Цитата Сообщение от Shamil1 Посмотреть сообщение
Не понял.
Ну любой вызов - по меньшей мере десятки машинных команд, по сравнению с 2-3 при тупеньком поиске. По сути все сведется к качеству реализации используемого ассоциативного контейнера. Как всегда есть альтернатива - сортировка плюс lookup. И тоже неясно как долго она будет сортировать на гигабайтах. Поэтому вариант с прогонами по строке мне кажется предпочтительнее
0
1 / 1 / 0
Регистрация: 01.11.2019
Сообщений: 31
14.11.2023, 10:39  [ТС]
В общем шляпа. На входных данных, близких к ограничениям считает быстро. 20 000 новых символов находит за 2 сек. Но попадаються такие тесты, где скорее всего колизия выскакивает (не находит хеш в unordered_map, хотя должен). Что с ней делать я без понятия. При увиличении переменных p и m, увеличивается кол-во найденных символов, но это такое себе.
Код на плюсах. Сначала заполняю patterns хешами, буквами и количеством для каждой буквы. После этого, заполняю pats(хеш и самая частая буква). Дальше прохожу 20к итераций постепенно прибавляя найденные символы в конец строки, тем самым обновляя pats. После этого в планах найти циклическую подстроку и сгенерировать ответ(вроде понимаю как это сделать).

Но например для входных данных: (n,k,a,b)
1000000 23919 129765611 130373173
рандомные_буквы

Перед основным циклом(поиска новых букв), в pats 454197 УНИКАЛЬНЫХ ключей. И каким образом в строке 10^6 букв может быть столько ключей при k=23919? При больших k и n=10^6, работает шустро и находит 2ляма новых символов за 1 сек. Может в коде намудрил, я не знаю. Как решить проблему?

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <iostream>
#include <string>
#include <cmath>
#include <unordered_map>
#include <vector>
#include <map>
#include <chrono>
#include <iomanip>
#include <string_view>
using namespace std;
const long long P = 99999773;
const long long m = 10000000019;
long long H(string_view s)
{
    long long hashh = 0;
    for(int i=0;i<s.size();i++)
    {
        hashh=(hashh*P+s[i])%m;
    }
    return hashh;
}
long long RH(long long prevHash, char prevPat, char newPat, long long multiplier)
{
    long long hashCode = prevHash;
    hashCode+=m;
    hashCode-=(multiplier*prevPat)%m;
    hashCode*=P;
    hashCode+=newPat;
    hashCode%=m;
    return hashCode;
}
int main()
{
    long long n,k,a,b;
    string s;
    cin>>n>>k>>a>>b>>s;
    auto start_time = chrono::high_resolution_clock::now();
    long long multiplier = 1;
    string_view pt{s};
    for(int i=1;i<k;i++)
    {
        multiplier=multiplier * P;
        multiplier=multiplier % m;
    }
    long long ph = H(pt.substr(n-k,k));
    unordered_map<long long, unordered_map<char,int>> patterns;
    unordered_map<long long,char>pats;
    long long start_hash=H(pt.substr(0,k));
 
    int iter=0;
    for(int i=0;i+k<n;i++)
    {
        patterns[start_hash][pt[i+k]]++;
        start_hash=RH(start_hash,pt[i],pt[i+k],multiplier);
        iter=i;
    }
    for(auto s:patterns)
    {
        int flag=0;
        char flg;
        for(auto d:s.second)
        {
            if(patterns[s.first][d.first]>flag)
            {
                flag=patterns[s.first][d.first];
                flg=d.first;
            }
            if(patterns[s.first][d.first]==flag)
                flg=min(flg,d.first);
        }
        pats[s.first]=flg;
    }
 
    int flag=0;
    char flg;
    for(int i=1;i<=20000;i++)
    {
        if(pats.count(ph)==0)
        {
            s.push_back('a');
            pt=string_view{s};
            start_hash=ph;
            ph=RH(ph,pt[s.size()-k-1],pt[s.size()-1], multiplier);
            patterns[start_hash][pt[s.size()-1]]++;
            for(auto df:patterns[start_hash])
            {
                if(patterns[start_hash][df.first]>flag)
                {
                    flag=patterns[start_hash][df.first];
                    flg=df.first;
                }
                if(patterns[start_hash][df.first]==flag)
                    flg=min(flg,df.first);
            }
            pats[start_hash]=flg;
            flg=NULL;
            flag=0;
            continue;
        }
        s.push_back(pats[ph]);
        pt=string_view{s};
        start_hash=ph;
        ph=RH(ph,pt[s.size()-k-1],pt[s.size()-1],multiplier);
 
        patterns[start_hash][pt[s.size()-1]]++;
        for(auto df:patterns[start_hash])
        {
            if(patterns[start_hash][df.first]>flag)
            {
                flag=patterns[start_hash][df.first];
                flg=df.first;
            }
            if(patterns[start_hash][df.first]==flag)
                flg=min(flg,df.first);
        }
        pats[start_hash]=flg;
        flg=NULL;
        flag=0;
    }
 
    auto end_time = chrono::high_resolution_clock::now();
    auto duration = chrono::duration_cast<chrono::duration<double>>(end_time - start_time);
 
    cout<<pt.substr(n+19000,1000)<<"\n";
    cout<<"Patterns: "<<sizeof(patterns)/1024.0<<" KB\n";
    cout<<"Pats size: "<<pats.size()<<"\n";
    cout<<"Patterns size: "<<patterns.size()<<"\n";
    cout << fixed << setprecision(2) << "Program execution time: " << duration.count() << " seconds" << endl;
    return 0;
}
Добавлено через 5 минут
Пробовал и такое, при больших k, иногда словарь заполняет долго(20 сек.) иногда быстро (2-3 сек).

Добавлено через 7 минут
Думаю за 5 сек. не вытянет. Наткнулся на тест, где нужно было найти около ляма новых букв. В ответе на тест было 2 огромных цикличных подстроки.

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

Добавлено через 4 минуты
Попробовал, все тесты в теории проходит, кроме n=10^6 и k до +-30000. Если k большое, находит 1 лям новых символов очень быстро(до 1 сек.).
0
1 / 1 / 0
Регистрация: 01.11.2019
Сообщений: 31
14.11.2023, 17:14  [ТС]
Shamil1, Пробовал и такое, при больших k, иногда словарь заполняет долго(20 сек.) иногда быстро (2-3 сек).

Добавлено через 1 минуту
Igor3D, Думаю за 5 сек. не вытянет. Наткнулся на тест, где нужно было найти около ляма новых букв. В ответе на тест было 2 огромных цикличных подстроки. Просмотрев тесты, составители задачи видимо хотят поиск за O(1).

Добавлено через 26 секунд
Mikhaylo, k - фиксированное, каждый раз берется подстрока из k последних символов входной строки, находится самая частая буква после подстроки во входной строке и добавляется в конец входной строки и т.д.

Добавлено через 51 секунду
igorrr37, Попробовал, все тесты в теории проходит, кроме n=10^6 и k до +-30000. Если k большое, находит 1 лям новых символов очень быстро(до 1 сек.).
0
1968 / 824 / 115
Регистрация: 01.10.2012
Сообщений: 4,868
Записей в блоге: 2
15.11.2023, 14:34
Владислав2313, если прозвучало "большие данные", то мы не можем надеяться даже на то что строка вся/целиком сядет в память (а не пойдет по свопам). Не говоря уже о таком дорогом удовольствии как ассоциативной контейнер. Тем более "контейнер на контейнер". Вот для определения зацикливания - там да, используйте что угодно. Но не для поиска.

Цитата Сообщение от Владислав2313 Посмотреть сообщение
Igor3D, Думаю за 5 сек. не вытянет.
В любом случае хорошо начинать с "нулевого варианта", тогда, по крайней мере, ясно что чего стоит. Ну и делайте прямой поиск для k = 1, без затей, только с аккуратным (без контейнеров) нахождением самого частого. Увидите/осознаете результаты - можно и "думать"
0
 Аватар для vantfiles
1018 / 1914 / 177
Регистрация: 07.05.2013
Сообщений: 3,931
Записей в блоге: 12
15.11.2023, 19:52
Цитата Сообщение от Владислав2313 Посмотреть сообщение
хотят поиск за O(1)
Может быть за O(n) ?

Я может быть неправильно все понял, но попробую описать, что мне кажется нужно проделать.

1. С помощью КА пробегаем по строке и находим позиции подстроки. Кладем в массив или вектор.
2. Зная длину подстроки определяем самый частый символ - и добавляем его в конец.
3. Удаляем из массива те индексы, в которых этого символа в нужной позиции нет.
4. Инкрементим позиции в массиве.
5. Повторяем начиная с п.2 нужное количество раз.

Добавлено через 7 минут
Если допустимы перехлёсты - то КА придется откатывать на следующий символ от начала найденной подстроки.
1
1 / 1 / 0
Регистрация: 01.11.2019
Сообщений: 31
15.11.2023, 20:29  [ТС]
Цитата Сообщение от vantfiles Посмотреть сообщение
Может быть за O(n) ?
Именно поиск - за O(1), иначе за 5с. не получиться найти 10^6 новых символов. Почему так? В ответах на некоторые тесты, исходная строка начинает повторяться только спустя 450 000 новых символов, а нам нужно найти эту самую циклическую строку и из нее сгенерировать ответ. В условии задачи - растояние от последнего символа входной строки до нулевого символа строки-ответа может быть +- 10^17.

Добавлено через 1 минуту
vantfiles, С КА еще не работал. Почитаю что это такое.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
15.11.2023, 20:29
Помогаю со студенческими работами здесь

Работа с большими данными
Добрый день. Существуют ситуации, когда нужно подгрузить и распарсить что-то очень большое, ну, например, адреса Москвы. Если поставить...

Нахождение количества всех возможных подстрок в строке s
На вход подается строка, вывести количество всех возможных подстрок, составленных из этой строки. Помогите реализовать

Работа с большими данными (30-40tb)
Всем привет. Столкнулся с такой проблемой. У меня есть большое количество данных и rest api. Эта апишка должна уметь загружать файл и...

Советы по оптимизации роботы с большими массивами.
Интересуют методы оптимизации работы с большими массивами Обрабатываются большие (несколько мегабайт) текстовые файлы. Они считываются...

Работа с данными в строке
Здравствуйте! Помогите, пожалста, решить задачу по работе с данными в строках на VBA. Нужно подсчитать количество групп цифр в заданной...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
SDL3 для Web (WebAssembly): Работа со звуком через SDL3_mixer
8Observer8 08.02.2026
Содержание блога Пошагово создадим проект для загрузки звукового файла и воспроизведения звука с помощью библиотеки SDL3_mixer. Звук будет воспроизводиться по клику мышки по холсту на Desktop и по. . .
SDL3 для Web (WebAssembly): Основы отладки веб-приложений на SDL3 по USB и Wi-Fi, запущенных в браузере мобильных устройств
8Observer8 07.02.2026
Содержание блога Браузер Chrome имеет средства для отладки мобильных веб-приложений по USB. В этой пошаговой инструкции ограничимся работой с консолью. Вывод в консоль - это часть процесса. . .
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru