Это является частью серии объяснений решения LeetCode ( index ). Если вам понравилось это решение или нашли его полезным, Пожалуйста, нравится Этот пост и/или upvote Мое решение по сообщению на форумах LeetCode Анкет
Проблема LeetCode #923 (Средняя): 3SUM с множественностью
Описание:
( прыгнуть в : Идея решения Код : JavaScript | Python | Java | C ++
Учитывая целочисленное массив arr
и целое число цель
, вернуть количество кортежей Я, J, K
тако, что я
arr [i] + arr [j] + arr [k]
Анкет
Как ответ может быть очень большим, вернуть его Modulo 10^9 + 7
Анкет
Примеры:
Вход: | arr = [1,1,2,2,3,3,4,4,5,5], |
Выход: | 20 |
Объяснение: | Перечисление по значениям (arr [i], arr [j], arr [k]): (1, 2, 5) происходит 8 раз; (1, 3, 4) происходит 8 раз; (2, 2, 4) происходит 2 раза; (2, 3, 3) происходит 2 раза. |
Вход: | arr = [1,1,2,2,2,2], |
Выход: | 12 |
Объяснение: |
Ограничения:
- 3. Length
- 0 [i]
- 0
Идея:
( Прыгните к : Описание задачи Код : JavaScript | Python | Java | C ++
Наивный подход здесь заключается в том, чтобы попробовать все перестановки, но это будет соответствовать 2.7e10 попытки. Первая важная вещь, которую нужно заметить, это то, что диапазон чисел очень мал в [0,100] . С этими несколькими номерами, любой большой массив ввода ( a ) будет иметь много дубликатов, что означает, что мы смотрим на Комбинаторика решение.
Однако для того, чтобы использовать сочетание комбинаториков, нам сначала нужно сделать частотная карта чисел в A . Мы всегда могли бы использовать стандартную карту для этого, но, поскольку диапазон чисел такой маленький и 0-индексированное , имеет смысл использовать массив вместо.
После того, как мы переехали через A и заполнила нашу номера карту ( nmap ) с частотами чисел, мы можем добраться до реальной работы. Нормальным подходом здесь будет выяснить различные доступные числа и использовать вложенные петли, чтобы попытаться каждую возможную перестановку. Но вместо того, чтобы делать это, что потребовало бы много вызовов массива, мы можем снова воспользоваться тем, что диапазон чисел такой мал.
Мы можем переиграть каждую возможную перестановку от [0,100] , независимо от того, что цифры в A Анкет Поскольку мы сделали частотную карту, эти числа будут представлены как 0 ‘, что будет легко предотвратить что -либо в наш ответ ( ans ) для перестановки, которые мы не можем сделать, и, используя простую математику вместо многих вызовов массива, мы можем быть более исполнительными.
Тем не менее, есть способы оптимизировать этот процесс. Основным подходом будет использование 2-указательская система Чтобы найти два наших значения, а затем математически представьте третье место, прежде чем применять правильную формулу перестановки к значениям.
Должно быть очевидно, что наше самое большое значение ( k ) никогда не может превышать цель ( t ), и это не может быть, очевидно, превышать максимальное значение 100 , чтобы мы могли начать это в Мин (т, 100) и уменьшение оттуда. Кроме того, поскольку он всегда будет представлен самым большим из трех значений, он никогда не может идти ниже T/3 , потому что тогда два меньших числа никогда не смогут поднять его до T Анкет
Переходя к следующему значению ( J ), мы видим, что оно никогда не может быть больше, чем k По определению, а также не может быть больше, чем оставшееся количество пространства ( rem ) осталось T , поэтому мы должны начать это в Мин (REM, K) Анкет Похоже на k , J Также никогда не может пойти ниже rem/2 Анкет
Как только у нас будет два из трех значений, мы можем проверить их частоты. Если кто -то из них 0 S, тогда он автоматически сделает результат своей перестановки, проверьте 0 также. Мы также потенциально можем сохранить некоторую обработку, проверив на 0 ‘и Продолжение Перед применением комбинаторных формул.
Если я , тогда мы знаем это я Потому что J должен быть между i и k , поэтому нам придется использовать n Выберите 3 формула. Мы также должны проверить, являются ли какие -либо два других значения одинаковыми, тогда мы должны использовать (n Выберите 2) * (n Выберите 1) Анкет В противном случае мы можем просто использовать формулу простых комбинаций.
Тогда важно помнить, чтобы применить modulo до возвращение Анкет
Реализация:
JavaScript был на самом деле быстрее с подходом, который был выделял фактические различные элементы, сортировав их, а затем эффективно работал через комбинации, но код был намного длиннее и сложнее. Этот метод намного проще и почти так же быстр. В любом случае мы должны использовать напечатанный массив здесь для ArrayMap.
Java была странно медленнее на ярлыках итерации и фактически работала быстрее без дополнительных процессов.
Java и C ++ должны использовать длинный для их Анс (До возвращения, по крайней мере), и даже для nmap , в противном случае нам придется разыграть эти значения в длинный Каждый раз в любом случае.
Код JavaScript:
( Прыгните к : Описание задачи Идея решения
var threeSumMulti = function(A, T) { let nmap = new Uint16Array(101), third = T / 3, ans = 0 for (let i in A) nmap[A[i]]++ for (let k = Math.min(T, 100); k >= third; k--) { let rem = T - k, half = rem / 2 for (let j = Math.min(rem, k); j >= half; j--) { let i = rem - j, x = nmap[i], y = nmap[j], z = nmap[k], res if (i === k) res = x * (x-1) * (x-2) / 6 else if (i === j) res = x * (x-1) / 2 * z else if (j === k) res = x * y * (y-1) / 2 else res = x * y * z ans = (ans + res) % 1000000007 } } return ans };
Код Python:
( Прыгните к : Описание задачи Идея решения
class Solution: def threeSumMulti(self, A, T): nmap, third, ans = [0 for _ in range(101)], ceil(T / 3) - 1, 0 for num in A: nmap[num] += 1 for k in range(min(T,100), third, -1): rem = T - k half = ceil(rem / 2) - 1 for j in range(min(rem, k), half, -1): i = rem - j x, y, z = nmap[i], nmap[j], nmap[k] if i == k: ans += x * (x-1) * (x-2) // 6 elif i == j: ans += x * (x-1) // 2 * z elif j == k: ans += x * y * (y-1) // 2 else: ans += x * y * z return ans % 1000000007
Код Java:
( Прыгните к : Описание задачи Идея решения
class Solution { public int threeSumMulti(int[] A, int T) { long[] nmap = new long[101]; long ans = 0; for (int num : A) nmap[num]++; for (int k = 100; k >= 0; k--) for (int j = k; j >= 0; j--) { int i = T - k - j; if (i > j || i < 0) continue; long x = nmap[i], y = nmap[j], z = nmap[k], res = x * y * z; if (res == 0) continue; if (i == k) res = x * (x-1) * (x-2) / 6; else if (i == j) res = x * (x-1) / 2 * z; else if (j == k) res = x * y * (y-1) / 2; ans += res; } return (int)(ans % 1000000007); } }
C ++ Код:
( Прыгните к : Описание задачи Идея решения
class Solution { public: int threeSumMulti(vector& A, int T) { long nmap[101] = {0}, ans = 0; double third = T / 3; for (int num : A) nmap[num]++; for (int k = min(T, 100); k >= third; k--) { int rem = T - k; double half = rem / 2; for (int j = min(rem, k); j >= half; j--) { int i = rem - j; if (i > j || i < 0) continue; long x = nmap[i], y = nmap[j], z = nmap[k], res = x * y * z; if (res == 0) continue; if (i == k) res = x * (x-1) * (x-2) / 6; else if (i == j) res = x * (x-1) / 2 * z; else if (j == k) res = x * y * (y-1) / 2; ans += res; } } return (int)(ans % 1000000007); } };
Оригинал: “https://dev.to/seanpgallivan/solution-3sum-with-multiplicity-12ma”