This is a naïve implementation, but it's quite simple. We simply find all contiguous (non-empty) subarrays, and then filter to find those whose sums match our target:
const sum = (ns) => ns.reduce((a, b) => a + b, 0)
const suffixes = (xs) => xs .map ((_, i) => xs .slice (i))
const prefixes = (xs) => xs .map ((_, i) => xs .slice (0, i + 1))
const subArrays = (xs) => suffixes (xs) .flatMap (prefixes)
const subArraysWithTotal = (ns, t) => subArrays (ns) .filter ((s) => sum (s) == t)
console .log (
subArraysWithTotal ([1, 2, 4, 7], 7)
)
sum
is obvious, just adding together the numbers in an array.
prefixes
and suffixes
are similar, and should be clear with an example:
prefixes (['w', 'x', 'y', 'z'])
//=> [['w'], ['w', 'x'], ['w', 'x', 'y'], ['w', 'x', 'y', 'z']]
suffixes (['w', 'x', 'y', 'z'])
//=> [['w', 'x', 'y', 'z'], ['x', 'y', 'z'], ['y', 'z'], ['z']]
subArrays
combines these, returning all the prefixes of every suffix, which gives us all contiguous subarrays of the original:
subArrays (['w', 'x', 'y', 'z'])
//=> [['w'], ['w', 'x'], ['w', 'x', 'y'], ['w', 'x', 'y', 'z'],
// ['x'], ['x', 'y'], ['x', 'y', 'z'], ['y'], ['y', 'z'], ['z']]
Our main function, subArraysWithTotal
simply finds all these subArrays and then filters them by calculating their sums, comparing that to our target.
As I said, this is naïve. It will be O(n^2)
in space and O(n^3)
in time. If we knew that all our numbers were positive, some sort of sliding window technique would definitely improve on both the speed and memory of this. With the possibility of negative numbers, there is still probably a less expensive algorithm, but it's not nearly as obvious.
But we can reduce the memory of this using generator functions. An alternate version replacing all of the above (except sum
) with generators, and adding a filter
function for generators, might look like this:
const sum = (ns) => ns.reduce((a, b) => a + b, 0)
function* suffixes (xs) {for (let i of xs .keys ()) yield xs .slice (i)}
function* prefixes (xs) {for (let i of xs .keys ()) yield xs .slice (0, i + 1)}
function* subs (xs) {for (let suff of suffixes (xs)) yield* prefixes (suff)}
function* filter (fn, it) {for (let x of it) {if (fn (x)) yield x}}
function* subArraysWithTotal (ns, t) {yield * filter ((s) => sum (s) == t, subs (ns))}
console .log (
[...subArraysWithTotal ([1, 2, 4, 7], 7)]
)
Now we need only enough memory to hold the current subarray.