Вместо hash_set с некоторых пор рекомендуется использовать
unordered_set (тоже использует хеш-таблицу)
А вместо hash_map - unordered_map
Хеш-таблица
Хеш-функция
15_FebS. CENSOR_SILVER
http://dl.gsu.by/task.jsp?nid=1421155&cid=15
Фермер Джон купил подписку журнала Good Hooveskeeping для своих коров, теперь им есть что почитать. К несчастью, последний номер содержит довольно неподходящую статью, как приготовить совершенный бифштекс. ФД хочет чтобы его коровы не увидели эту статью.
ФД взял текст из журнала и создал строку S длиной не более чем 10^6 символов. Из неё он хочет удалить все вхождения подстроки T длиной <= 100 символов неподходящего содержания. Чтобы сделать это, ФД ищет первое вхождение T в S и удаляет его. Затем он повторяет процесс опять, снова удаляя первое вхождение T, продолжая так до тех пор, пока больше не станет вхождений T в S. Заметим, что удаление одного вхождения может создать другое вхождение, которое не существовало раньше.
Пожалуйста, помогите ФД определить конечное содержание строки S после завершения всех удалений.
INPUT FORMAT: (файл censor.in)
Первая строка содержит S. Вторая строка будет содержать T. Длина T не более чем длина S, и все символы S и T - маленькие латинские буквы (a..z).
OUTPUT FORMAT: (файл censor.out)
Строка S после завершения всех удалений. Гарантируется, что S не станет пустой после завершения процесса всех удалений.
SAMPLE INPUT:
whatthemomooofun
moo
SAMPLE OUTPUT:
whatthefun
Наивное решение (делать что написано)
#include <bits/stdc++.h>
using namespace std;
int main()
{
freopen("censor.in","r",stdin);
freopen("censor.out","w",stdout);
string s,t;
int k,d;
cin >> s >> t;
d=t.length();
k=s.find(t);
while (k>=0)
{s.erase(k,d); k=s.find(t); }
cout << s;
}
Проходит только первые 2 теста (остальные не проходит по времени).
Для ускорения, рассмотрим построение результирующей строки R, по одному символу за раз.
Если конец R совпадает с T, мы должны удалить его из R.
Поскольку это удаление с конца, оно выполняется за O(1) - resize (изменить длину)
Вот текст соответствующей программы
#include <bits/stdc++.h>
using namespace std;
int main()
{
freopen("censor.in", "r", stdin);
freopen("censor.out", "w", stdout);
string S, T;
cin >> S >> T;
string R;
for (int i = 0; i < S.size(); i++)
{
R += S[i];
if ((R.size() >= T.size()) &&
(R.substr(R.size() - T.size()) == T))
R.resize(R.size() - T.size());
}
cout << R;
}
Оно тоже не проходит по времени, начиная с 3-го теста
Теперь основная проблема - эффективно определить совпадает ли конец строки R со строкой T.
Сделаем это хешированием.
#define HM 1000000007
#define HA 100000007
#define HB 101
/*По заданному хешу h строки S, вычисляем хеш строки S + 'ch' */
int hext(int h, int ch)
{
return (1ll * h * HA + ch + HB) % HM;
}
Здесь
HA и HB константы полиномиального хеша
HM - модуль, по которому вычисляется хеш
1ll - константа типа long long (длинное целое - до 2^63),
обеспечивающая вычисления в диапазоне long long
Мы должны вычислить хеш от T
и дополнительно вычислять rolling hash конца R.
Под rolling hash понимается последовательное вычисление хеша строки при добавлении символов к её концу
Полный текст решения
#include <bits/stdc++.h>
using namespace std;
#define HM 1000000007
#define HA 100000007
#define HB 101
int hext(int h, int ch)
{
return (1ll * h * HA + ch + HB) % HM;
}
int main() {
freopen("censor.in", "r", stdin);
freopen("censor.out", "w", stdout);
string S, T;
cin >> S >> T;
int thsh = 0;
for (int i = 0; i < T.size(); i++)
thsh = hext(thsh, T[i] - 'a');
string R;
vector<int> rhsh(1, 0);
vector<int> HAPW(1, 1);
for (int i = 0; i < S.size(); i++)
{
R += S[i];
rhsh.push_back(hext(rhsh.back(), S[i] - 'a'));
HAPW.push_back((1ll * HAPW.back() * HA) % HM);
if (R.size() >= T.size())
{
int hsub = (1ll * rhsh[R.size() - T.size()] * HAPW[T.size()]) % HM;
int hsh = (HM + rhsh.back() - hsub) % HM;
if (hsh == thsh && R.substr(R.size() - T.size()) == T)
{
R.resize(R.size() - T.size());
rhsh.resize(rhsh.size() - T.size());
HAPW.resize(HAPW.size() - T.size());
}
}
}
cout << R << endl;
return 0;
}