Advertisement
jorupp

https://leetcode.com/problems/count-the-number-of-square-free-subsets

Aug 7th, 2023
16
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.25 KB | None | 0 0
  1. function squareFreeSubsets(nums: number[]): number {
  2. const mod = (Math.pow(10, 9) + 7);
  3.  
  4. // core approach to this - modular arithemetic - see https://en.wikipedia.org/wiki/Modular_arithmetic#Integers_modulo_n
  5. // specifically, these simplification rules:
  6. // (a + b) % n === (a % n) + (b % n)
  7. // (a * b) % n === (a % n) * (b % n)
  8. // ie. when you're just doing addition and multiplication, you can apply the modulus operation right away
  9. // you don't have to wait until the end to apply it. This can make sure you never have to operate
  10. // on any number bigger than mod*mod
  11.  
  12. // however, in Javascript, even mod*mod is too big to fit in a fully-precise number - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_encoding
  13. // to fix that, we use this method to split that into two separate multiplications
  14. // the first divided by 1000, and the second with just that leftover 1000 part
  15. // taking advantage of the fact that (a+b)*c === (a*c)+(b*c)
  16. // which we can then use modular artithmetic rules to prevent an overflow
  17. function safeModMult(v1: number, v2: number) {
  18. if (v1 > v2) {
  19. return safeModMult(v2, v1);
  20. }
  21. var shift = Math.pow(10, 4);
  22. var part1 = (Math.floor(v1/shift) * (v2 % mod)) % mod;
  23. var part2 = ((v1 % shift) * (v2 % mod)) % mod;
  24. return ((part1 * shift) % mod + part2) % mod;
  25. }
  26.  
  27. // I'm sure there's a mathematical way to calculate this, but I recognized pascal's triangle and just went with that
  28. const triangles = new Map<number, number[]>();
  29. triangles.set(0, [1]);
  30. function getTriangle(n: number): number[] {
  31. if (triangles.has(n)) {
  32. return triangles.get(n);
  33. }
  34. const prev = getTriangle(n-1);
  35. const next = [1];
  36. for(let i=1; i<prev.length; i++) {
  37. next.push((prev[i] + prev[i-1]) % mod);
  38. }
  39. next.push(1);
  40. triangles.set(n, next);
  41. return next;
  42. }
  43.  
  44. // can't use any numbers that are themselves square or a multiple of a square
  45. const squares = Array.from(new Array(25)).map((i, ix) => ix + 2).map(i => i*i);
  46. const candidates = nums.filter(i => !squares.some(ii => i % ii === 0));
  47.  
  48. // we also can't use any number twice, so we'll track duplicates
  49. const counts = new Map<number, number>();
  50. for(const i of candidates) {
  51. if (counts.has(i)) {
  52. counts.set(i, counts.get(i) + 1);
  53. } else {
  54. counts.set(i, 1);
  55. }
  56. }
  57.  
  58. // for each unique number, there are count+1 valid configs for it (none present, exactly 1 present)
  59. // so we just multiply them all together (and subtract one because "none present" for all of them isn't valid)
  60. // const sum = Array.from(counts.values()).reduce((a,b) => a * (b+1), 1) - 1;
  61. // however, this doesn't work in situations where there's multiple numbers with a prime inside them
  62. // ie. [2,6] - we can't keep them both, as that'd result in 12, which has a 4 in it.
  63. // we also have to account for the fact that we can keep as many 1s as we want
  64.  
  65. const allNumbers = Array.from(counts.keys());
  66. function getSubsets(soFar: number[], ix: number) {
  67. if (ix >= allNumbers.length) {
  68. return soFar.length ? 1 : 0;
  69. }
  70. const n = allNumbers[ix];
  71. const count = counts.get(n)!;
  72.  
  73. let r: number;
  74. // 1 is special, since we can add as many or as few of them as we like
  75. if (n === 1) {
  76. const combo1s = getTriangle(count).reduce((a,b) => (a+b)%mod,-1);
  77. r = getSubsets(soFar, ix+1) + safeModMult(combo1s, getSubsets([...soFar, n], ix+1));
  78. }
  79. else if (soFar.some(i => squares.some(s => (i * n) % s === 0)))
  80. {
  81. // this number would conflict with an earlier one - can't add any
  82. r = getSubsets(soFar, ix+1);
  83. }
  84. else
  85. {
  86. // we _can_ add this, but we don't have to
  87. r = getSubsets(soFar, ix+1) + safeModMult(count, getSubsets([...soFar, n], ix+1));
  88. }
  89.  
  90. return r % mod;
  91. }
  92.  
  93. return getSubsets([], 0);
  94. };
  95.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement