#string #reverse
#строка #обратный
Вопрос:
Недавно во время какого-то интервью спросили: «Как найти обратное всех строк, если существует в списке из более чем миллиона строк?
Например, для str[1] = «abc» мне нужно точно проверить наличие «cba», никаких анаграмм.
Способ 1. Сохраните все строки в hashset, начните обход с первой строки и проверьте, существует ли в Hashset перевернутая форма. если да, то пара else переходит к следующему элементу.
Можете ли вы предложить какой-либо метод, если ограничением является память?
Комментарии:
1. При повторном чтении неясно, хотите ли вы найти все строки, которые являются обратными по отношению к другим в том же списке, или, учитывая строку, найдите в списке строку, которая является ее обратной. Последнее, конечно, является простой проблемой поиска после того, как вы перевернете заданную строку.
2. Хотя я согласен с Дэниелом в этом, рассматривая ПАМЯТЬ как ограничение, это не имело бы никакого значения.
3. @DanielRHicks Я отредактировал свой вопрос…. он имел в виду, что для всех строк в списке найдите, существует ли обратная ему…
Ответ №1:
Если разрешено, вы можете отсортировать строки на месте, чтобы при поиске обратной строки вы могли выполнить двоичный поиск.
Ответ №2:
Вы можете использовать фильтр Блума, который сообщит вам, существует ли строка уже в структуре, подобной хэш-таблице, но каждый сегмент равен только 0 или 1, поэтому используется очень мало места.
Ровно 1 000 000 бит == 125 КБ
Комментарии:
1. 1.) это займет больше памяти. 2) вам не нужна длинная строка, чтобы получить много строк одинаковой длины.
Ответ №3:
Вы можете выбрать хэш-таблицу и использовать сегменты, чтобы уменьшить конфликт хэшей. Что нам теперь нужно сделать для конкретной строки запроса, это просто перевернуть ее, хэшировать и найти в хэш-таблице вместо того, чтобы проходить от начала до конца.
Комментарии:
1. Да, это по сути то же самое, что и моя схема, только с вдвое большим количеством хэшей.
Ответ №4:
Это просто мое мнение:
Я бы создал хэш с
ключ = символ
значение = Список строк, которые начинаются с этого символа
- Теперь запустите цикл, внутри которого вам нужно начать с первой строки.
- переверните его
- Возьмите первый символ и найдите этот ключ в хэше
- затем в значении этого он содержит список строк и находит строку в этом списке
Ответ №5:
С «памятью как ограничением» я бы даже не стал использовать HashSet (который, afaik, также удалит дублированные строки в исходном списке), потому что вы будете использовать дополнительную структуру HashSet, которая занимает некоторое количество памяти.
Сортировка также не улучшит использование памяти.
Я бы использовал исходный список (который уже есть, поэтому дополнительная память не будет использоваться) 3-байтовую целочисленную переменную для итерации списка. 3 байта могут выполнять итерацию по списку из 2 ^ 24 = 16777216 строк
С «памятью как ограничением» я бы выбрал 2 цикла for. Я думаю, что псевдокод, подобный C, будет легче понять, что мой простой английский.
Примечания:
- Из примера, приведенного в вопросе, на самом деле это не список, а массив, поэтому я буду работать со структурой, как если бы это был массив
- Вопрос не ясен, как связать эти «abc», «def»,»cba»,»abc». Я буду соединять первый «abc» с «cba», а также этот «cba» со «вторым «abc» (намерение неясно в вопросе)
- Я предполагаю, что мы не можем изменить исходный список
Вот код с наименьшим потреблением памяти, который я могу придумать:
// "list" holds the original list (array)
for (int i = 0; i < length(list) - 1; i ) {
for (int j = i 1; j < length(list); j ) {
if (list[i] == reverse(list[j])) {
print(list[i] " reversed is " list[j])
}
}
}
Что касается использования памяти, это решение будет принимать 2 целочисленные переменные (обычно по 4 байта каждая) исходный список, от которого, я полагаю, мы не можем избавиться.
Что касается использования процессора (на самом деле, не имеет значения, исходя из вопроса), количество раз, когда строки будут отменены, будет: (N * (N 1)) / 2, где N — длина списка
Комментарии:
1. 1,000,000,000,000 итераций, больше или меньше. (Не считая фактического цикла сравнения.)
2. Хм, нет. Всего 1 итерация по списку. Порядок этого решения — N. Но, как я уже сказал, и человек, который спросил, четко указал, нет необходимости делать это быстро, но с наименьшим объемом памяти. Список уже есть, я просто добавляю 3 байта. Сколько дополнительных байтов занимает ваше решение?
3. Поэтому, пожалуйста, объясните, как за один проход по списку вы идентифицируете все обратные дубликаты в списке.
4. Дэниел, твой последний комментарий заставил меня понять первоначальный вопрос. Я подумал, что, как и в примере в вопросе, у вас уже была строка, и идея заключалась в том, чтобы проверить, содержится ли в списке обратная форма этой (единственной) строки. Абсолютно моя ошибка. Удаление моего ответа.
Ответ №6:
Сначала я бы хэшировал строки, используя хэш, который не зависит от направления. Это может быть простая сумма символов, хотя, безусловно, существуют лучшие схемы, которые будут хэшировать с обоих концов. И чтобы «подсластить сделку», можно добавить длину строки к хэш-значению или иным образом включить ее в хэш.
Затем, когда строки будут разбиты на идентичные хэш-группы, выполните сравнение «длинной руки».
Обратите внимание, что, используя эту схему или ту, где вы просто используете хэш, зависящий от направления, вперед или назад, нужно не сразу вставлять строку в набор хэшей, а сначала проверить ее (с обратным хэшем, если необходимо), и если вы получите совпадение (ипоследующее длинное сравнение истинно) удалите уже хэшированную строку и соедините две. Вторая строка никогда не попадает в набор, и, если все строки имеют совпадения максимум, у вас будет только 500 000 записей в хэш-наборе, и, если строки были случайными, вероятно, ближе к 250 000 (я не садился, чтобы вычислить вероятности).
Таким образом, вам потребуется только один проход через набор строк, чтобы сделать все это.
Комментарии:
1. использование хэш-значения, не зависящего от направления, не дает вам никаких реальных преимуществ, но, безусловно, увеличит коэффициент коллизий.
2. Независимый от направления хэш хэширует «abc» и «cba» в одну корзину. Это значительно сокращает количество комбинаций, которые вам нужно попробовать.
3. Я не понимаю. Почему это что-то уменьшает? О каких комбинациях вы говорите?
4. С помощью этой схемы нужно только сравнивать строки с одинаковым хэшем. Я бы предположил, что вы могли бы организовать получение не менее 5000 различных хэшей, чтобы среднее количество строк, которые вам нужно было бы сравнить с другими, было порядка 200 против 1 000 000. И небольшое усилие может привести к созданию алгоритма хеширования, который будет работать намного лучше.
5. Вы имеете в виду, что хотите генерировать много коллизий хэшей? Какой в этом смысл? Вам нужен связанный список для хранения результатов, поэтому вы не будете использовать меньше памяти, но из-за коллизий определенно используете больше процессора. В этом случае я предпочитаю решение OP, поскольку оно превосходит.