Дан массив nums
Считать нет. пар (два элемента), где побитовое И больше, чем K
Грубая сила
for i in range(0,n):
for j in range(i+1,n):
if a[i]&a[j] > k:
res += 1
Лучшая версия:
предварительная обработка для удаления всех элементов ≤k
а потом грубая сила
Но мне было интересно, каков будет предел сложности здесь?
Можем ли мы добиться большего успеха с подходом trie, hashmap, например с двумя суммами?
(Я не нашел эту проблему на Leetcode, поэтому решил спросить здесь)
Хм, вы могли бы использовать решение этой задачи плюс включение-исключение для решения Задачи ортогональных векторов , поэтому получить значительно субквадратичное решение без фальсификации гипотезы сильного экспоненциального времени (большой открытый вопрос в теории сложности) будет сложно. Когда подобные проблемы возникают в соревновательном программировании, обычно существует ограничение на размер слова, которое разблокирует технику суммирования по подмножествам.
@yozaam: Источник этого вопроса, пожалуйста?
@ShridharRKulkarni зайдите на канал #interview-experience, кто-то поделился им в предпоследнем сообщении, которое они получили по ссылке на веб-сайт онлайн-оценки: http://binarysearch.com/
@dmuir Я не думал об этом, хорошо!
Пусть size_of_input_array = N
. Пусть входной массив состоит из B
-битных чисел
Вот простое для понимания и реализации решение.
Удалите все значения <= k.
На изображении выше показаны 5 10-битных чисел.
Шаг 1: График смежности
Шаг 2: Задача состоит в том, чтобы избежать повторного подсчета одних и тех же пар.
Чтобы решить эту проблему, мы воспользуемся структурой данных union-find, как показано в коде ниже.
//unordered_map<int, vector<int>> adjacency_graph;
//adjacency_graph has been filled up in step 1
vector<int> parent;
for(int i = 0; i < input_array.size(); i++)
parent.push_back(i);
int result = 0;
for(int i = 0; i < adjacency_graph.size(); i++){ // loop 1
auto v = adjacency_graph[i];
if (v.size() > 1){
int different_parents = 1;
for (int j = 1; j < v.size(); j++) { // loop 2
int x = find(parent, v[j]);
int y = find(parent, v[j - 1]);
if (x != y) {
different_parents++;
union(parent, x, y);
}
}
result += (different_parents * (different_parents - 1)) / 2;
}
}
return result;
В приведенном выше коде find
и union
взяты из структуры данных union-find.
Сложность времени:
Шаг 1:
Build Adjacency Graph: O(BN)
Шаг 2:
Loop 1: O(B)
Loop 2: O(N * Inverse of Ackermann’s function which is an extremely slow-growing function)
Общая временная сложность
= O(BN)
Космическая сложность
Overall space complexity = O(BN)
«остальная часть проста» - это не то, что ищет OP. Кроме того, это не так просто, как вы думаете, так что объясните, пожалуйста.
@גלעדברקן Где-то я сомневался, что логика родительского массива будет работать постоянно. Я изменил его, чтобы использовать union-find. Помогите мне с временной сложностью. Я предполагаю, что в нашем случае Inverse Ackermann будет амортизировать тета (1).
Я хочу, чтобы за это решение была предложена награда. Довольно тяжелая работа. 😛
Спасибо, @ShridharRKulkarni, как мне назначить награду?
@yozaam: У тебя большой ♥️. Спасибо! Сохраните свою репутацию на ранних стадиях SO. Помогает зарабатывать привилегии и вносить свой вклад в SO лучше.
Не могли бы вы добавить тестируемый код со случайным вводом? (Или даже просто тестируемый код). Основываясь на комментарии Дэвида Эйзенстата , я не уверен, что верю, что это (или, по крайней мере, временная сложность, которую вы предлагаете) возможно.
Мне любопытно узнать о вашей обеспокоенности, а также о комментарии Дэвида. Хотя у меня мало времени. Вы или @yozaam можете протестировать код и сообщить о любых неудачных случаях. Пожалуйста, используйте С++. Теоретически решение выглядит хорошо. Буду продолжать работу, если будут сбои. Кроме того, мы можем начать чат вместе с Дэвидом, чтобы понять его комментарий. Если вы понимаете комментарий полностью, пожалуйста, помогите мне понять его тоже. Я очень хочу узнать об этом.
@ גלעדברקן Я не вижу, где в этом ответе используется k за пределами шага обрезки. Предполагая отсутствие ограничений на размер слова (что могло бы обойти SETH с помощью упомянутой мной техники суммирования по подмножествам), мы можем просто выделить уникальный старший бит для каждого входного числа и сделать шаг сокращения неактуальным (поскольку все числа будут ≥ k, но будет иметь то же побитовое И, что и раньше). Учитывая, что остальная часть алгоритма кажется независимой от k, мне трудно поверить, что он правильный. +1 к запросу кода.
Это становится интересным. На выходных буду кодить. Между тем, если кто-то может написать это на C++, это было бы здорово.
Обновление: я мог бы потратить некоторое время на написание кода. pastebin.com/TspKFyj7 Я нашел ошибку для ввода, который я упомянул в коде. Я пытаюсь это исправить. Проблема заключается в проверке битов <= (int)(log2(k)). Было бы здорово, если бы кто-то мог потратить некоторое время на обдумывание этой интересной ошибки, и я тоже попробую, когда и когда позволит время. Спасибо за ваше терпение. Я также думаю о возможности каких-либо альтернативных решений.
Во-первых, обрезать все <= k
. Также отсортируйте список значений.
Переходя от старшего бита к младшему, мы собираемся отслеживать набор чисел, с которыми мы работаем (изначально все s=0, e=n
).
Пусть p будет первой позицией, содержащей 1 в текущем наборе в текущей позиции.
Если бит в k равен 0, то все, что дает 1 мир, определенно будет хорошим, и нам нужно исследовать те, которые получают 0. У нас есть (end - p) * (end-p-1) /2
пар в текущем диапазоне и (end-p) * <total 1s in this position larger or equal to end>
комбинаций с большими ранее хорошими числами, которые мы можно добавить в решение. Для продолжения обновляем end = p
. Мы хотим посчитать единицы во всех приведенных выше числах, потому что раньше мы считали их только парами друг с другом, а не с такими младшими числами в наборе.
Если бит в k равен 1, то мы еще не можем подсчитать выигрыши, но нам нужно исключить все, что ниже p
, поэтому мы обновляем start = p
.
Вы можете остановиться, как только вы прошли все биты или start==end
.
Подробности:
p
.<total 1s in this position larger than p>
. У нас уже есть отсортированные значения. Таким образом, мы можем вычислить частичные суммы и сохранить для каждой позиции в отсортированном списке количество единиц в каждой битовой позиции для всех чисел над ней.Сложность:
Всего O(L logN + NlogN + L*N).
Поскольку N>>L, L logN
включается в NlogN
. Поскольку L>>logN (вероятно, у вас есть 32-битные числа, но их не 4 миллиарда), то NlogN
включается в L*N
. Таким образом, сложность составляет O (L * N). Поскольку нам также нужно сохранять частичные суммы вокруг памяти, сложность также составляет O (L * N).
Могут ли какие-либо элементы массива быть отрицательными? Если это так, вы не должны удалять их, например, в 16 битах 0xffff и 0x7fff равны 0x7fff.