Никак немогу понять всю пользу использования хэширования в задачах на строки. Складывается впечатление, что это нужно только для быстрого сравнения строк. Тем не менее, слышал, что многие задачи как-то очень просто решаются с использованием хэширования. Например:
UPD. Спасибо, всем, теперь появился немного другой вопрос: можно ли использовать hash_map на соревнованиях (точнее есть ли какие-то соревнования, где таковых библиотек нету)? А то вроде как они не стандартизованы?
http://acm.timus.ru/problem.aspx?space=1&num=1517 (умею решать за линейное время суффиксным деревом, слышал, что решается просто за O(nlogn) хэшами)
Если кому-то не трудно, напишите пожалуйста разбор данных задач, а так же может еще какие-то полезные идеи-задачи на эту тему.
[увеличваем_строку_чтобы_текст_нормально_читался_а_не_по_слову_в_строке]
бинпоиск не нужен. делаем цикл по длине от 1 до 100
X кидаем все подстроки длины k всех строк в хеш таблицу
X перебираем строки для которых еще нет ответа
XX выкидываем из хеш таблицы все подстроки длины k нашей фиксированной строки
XXX смотрим в хеше есть то что нас интересует или нет
XX добавляем в хеш таблицу все подстроки длины k нашей фиксированной строки
_______________Не_хочу_узкий_текст_____________
А я решил бинпоиском по длине. Далее перебираем все хеши строк длины k. Сортируем и находим есть ли совпадающие хеши.
Асимптотика nlog^2n (logn бинпоиск, nlogn - проверка).
Так что такое решение допустимо :)
http://acm.timus.ru/problem.aspx?space=1&num=1542
Хэш+Сет я делал.
Попробуй еще просто набор строк отсортировать по алфавиту за N*LogN*LogM, N - кол-во строк, M - длина строк. Бинарный поиск по совпадающему хэшу.
Еще хэшированием найти мининимальный циклический сдвиг. Тоже бинарный поиск по совпадающему хэшу.
Да и вообще много задач разных.
Получаем порядка n хэшей для каждой длины подстроки,поэтому сложность, по-моему, будет n*(n log n).
тогда букве в прямоугольнике a[i,j] сопоставим величину a[i,j]*p^i*q^j (i, j, конечно же, считаются от левой и верхней границ выбранного прямоугольника). хэшом будет сумма этих величин по всему прямоугольнику.
как после предпросчета всей таблицы за О(1) находить хэш для любого прямоугольника - догадаться несложно...
Сорри, может я тупой!Но непонятно по задаче 1517.Что значит фиксируем длину ответа?
int l = 0, r = s.length();
примеры задач некоторые
1) http://acm.timus.ru/problem.aspx?space=1&num=1486
2) http://acm.timus.ru/problem.aspx?space=1&num=1590 - вообще подходит для любого алгоритма
3) http://acm.timus.ru/problem.aspx?space=1&num=1425 - не на строки, но я помню, сдавал хешами
Тут вообще много задач кинули, но если нужно еще задач интересных, пиши в личку)
Бинпоиск по длине ответа. Пусть длина k. Тогда вычисляем O(n) полиномиальных хэшей подстрок длины k первой строки за время O(n) и складываем их в хэш-таблицу. Потом проходимся по подстрокам длины k второй строки и смотрим - лежить ли в хэш-таблице нужный хэш. После проверки для k хэш-таблицу аккуратно очищаем.
Вторую задачу абсолютно аналогичным образом можно решить за O(N^2logN).
Вот может будет полезно, я как раз недавно читал лекцию по полиномиальным хешам:
http://skidanovalex.ru/slides/phashes.pptx
Нет, все слайды там. Может из-за того, что сохранял в 2010 powerpnt.
Попробуй
http://skidanovalex.ru/slides/phashes.ppt
Здесь нашим ребятам русскоговорящим. В неформальной обстановке :о)
Я просто не понял вопрос "какие именно хэши использовать?" - единственное, что пришло на ум - полиномиальные)
Ответ на новый вопрос: нет, hash_map есть не везде. Например, везде где в качестве компилятора стоит студия, его нет.
Встречный вопрос: зачем тебе hash_map? :о) Если мне не изменяет память (поправьте меня, если я не прав), там медленная реализация, и если обычный map не проходит, hash_map не панацея -- все равно надо писать хеш табличку ручками.
Разве нет в студии?
#include <hash_set>
#include <hash_map>
using namespace std;
using namespace stdext;
int main () {
hash_set <int> w;
hash_map <int, int> w2;
return 0;
}
Всё замечательно компилируется.
upd: про hash_map не знаю, но hash_set работает медленее примерно в два раза в сравнении с ручной реализацией (как то давно тестировал немного).
1) Могут сильно тормозить new / delete
2) Думаю, что асимптотика O(n^2 * log^2 (n)) недостаточно хороша
Мое решение O(n^2 * log(n)) с самописной hash_map без динамической памяти заходит за 0.3 с, а лишний log(n) раз в 8 замедляет решение -> TL
log(n) от бинпоиска
Вставка/поиск в дереве - log(n)
Вставка/поиск в hash_map - O(1)
Кстати, дерево у тебя без балансировки. 99%, конечно, что при хранении хешей его высота будет расти как O(log(n)), но все же
long long hash;
elem (long long _hash): hash(_hash) {}
{
return a.hash < b.hash;
}
А какое лучше использовать простое число для вычисления хеша?
(Слышал что при использовании маленьких латинских букв хватит 31)
модульp - просто числомодуляp близко размеру алфавита, желательно чуть больше. К примеру, для строчных английских букв отлично подходит что-то типа 29, 31.Я имел ввиду другое
h(S) = S[0] * P^N + S[1] * P^(N-1)+ S[2] * P^(N-2)+ S[3] * P^(N-3)+ ... + S[N]
Я подразумевал простое число P, которое вы возводим в степень, а не модуль.
Меня поражает до чего же глупы некоторые вопросы в этом топике.
Те, кто знает что такое хеш, ничего о нем не знает если не умеет написать хеш таблицу и не знает парадокса дней рождений. Кормена в руки и читать. Иногда проще написать хеш таблицу, чем бегать по громоздким итераторам мапы (недобно писать map<laja<blabla<int, LL>, int>, MyStruct>::iterator) и бояться использовать ссылки из-за того что память может быть перевыделена. Это такая же стандартная вешь как дейкстра или бфс, и ничем не сложнее.
Наблюдения, из своего опыта:
После того как прочитаете оба подхода разрешения коллизий из кормена, забудтье про второй. Для участия в олимпиадах самый лучший способ это использовать списки смежности.
полиномиальный хеш от строки s это (s[0] * m^0 + ... + s[n - 1] * m^(n - 1) ) % P
P - модуль, m - множилка. Хорошая множилка, это такая множилка которая генерирует большую группу по модулю P. умножая m на себя, мы войдём в цикл, и чем больше будет длина этого цикла тем лучше. Хороший модуль это большой модуль, для которого существуют крутые множилки.
для простых чисел выбранных в качестве модуля, есть множилки, с длиной цикла P - 1 (то есть генераторы всех чисел от 1 до P - 1)
просто ли число m или нет не важно, важно группу какого размера оно генерирует.
в качестве P иногда берут число 2^32 или 2^64, используя обычные операции с 32-х и 64-х битовыми операциями и забивая на переполнения (в этом случае у нас все действия как раз по указанным модулям происходят).
В этом случае для того чтобы число m хоть что-то генерировало оно должно быть нечётным, а простое оно или нет это никого не волнует. Нечётность – необходимое условие, а простота числа m не является ни необходимым условием, ни достаточным, для того чтобы получить большую группу. Вообще порядки всех элементов являются делителями Phi(P), где Phi(P) – функция эйлера.
Следовательно для модуля 2^32 порядки элементов могут быть только степенями двойки.
Чтобы вычислить порядок элемента, нужно возводить его в квадрат, пока оно не станет равно единице, 2^(количество шагов +-1 (лень думать)) и будет размером генерируемой группы.
Я юзаю для хешей некоторые числа которые помню и для которых знаю свойства, размеры групп, простоту или не простоту.
1517 заходит по одному хешу? писал по одному-wa 10 написал по двум — wa 32 юзал хеш таблицу я криворукий и можно написать по одному или нужно обязательно два писать?) но и в том случае я видимо накосячил.
Рассматриваемых подстрок примерно 105 => при модуле около 1010 скорее всего будет коллизия. Таким образом, точно заходит 1 модуль порядка 1018 <=> 2 модуля порядка 109.