Facebook Pixel

1237. Find Positive Integer Solution for a Given Equation

Problem Description

You are given a callable function f(x, y) with a hidden formula and a target value z. Your task is to find all positive integer pairs (x, y) such that f(x, y) == z.

The function f(x, y) has the following properties:

  • It takes two positive integers x and y as inputs
  • It returns a positive integer based on a hidden formula
  • It is monotonically increasing in both parameters:
    • f(x, y) < f(x + 1, y) (increasing with respect to x)
    • f(x, y) < f(x, y + 1) (increasing with respect to y)

You need to implement a findSolution method that:

  • Takes a CustomFunction object and an integer z as parameters
  • Returns a list of all [x, y] pairs where f(x, y) == z
  • The pairs can be returned in any order
  • Both x and y must be positive integers

The solution approach leverages the monotonic property of the function. Since f(x, y) is increasing in both dimensions, for each value of x from 1 to z, we can use binary search to find if there exists a y value such that f(x, y) == z. The binary search is performed on the range [1, z] for y, using the function value f(x, y) as the comparison key. If a valid y is found for a given x, the pair [x, y] is added to the result list.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight comes from understanding the monotonic property of the function. Since f(x, y) increases as either x or y increases, we can think of the function values as forming a 2D grid where values grow both rightward and upward.

Imagine we fix x at some value. As y increases from 1 onwards, f(x, y) will produce an increasing sequence of values. At some point, this sequence might contain our target z, or it might skip over it entirely. Because the sequence is sorted, we can efficiently search for z using binary search instead of checking every possible y value.

Why enumerate x and binary search y? We could do it either way, but we need to fix one variable to create a one-dimensional sorted sequence that we can binary search. By iterating through all possible x values from 1 to z, we ensure we don't miss any valid pairs.

Why can we limit both x and y to the range [1, z]? Since f(x, y) is monotonically increasing and both x and y are positive integers starting from 1, the minimum possible value is f(1, 1). If f(1, 1) ≤ z, then for any x > z or y > z, we would have f(x, 1) > f(z, 1) ≥ f(1, 1) ≥ z or similarly for y. This means f(x, y) would definitely exceed z when either parameter is greater than z.

The binary search finds the leftmost position where f(x, y) >= z. If the function value at that position equals z, we've found a valid pair. If it's greater than z, it means no y value for this particular x gives us exactly z.

Learn more about Math, Two Pointers and Binary Search patterns.

Solution Implementation

1from typing import List
2
3
4class Solution:
5    def findSolution(self, customfunction: 'CustomFunction', z: int) -> List[List[int]]:
6        """
7        Find all pairs [x, y] where f(x, y) == z.
8        For each x, use binary search template to find first y where f(x, y) >= z.
9        Pattern: false, false, ..., true, true (find first true)
10
11        Args:
12            customfunction: Custom function object with method f(x, y)
13            z: Target value to find
14
15        Returns:
16            List of [x, y] pairs where f(x, y) == z
17        """
18        result = []
19
20        for x in range(1, z + 1):
21            # Binary search: find first y where f(x, y) >= z
22            left, right = 1, z
23            first_true_index = -1
24
25            while left <= right:
26                mid = (left + right) // 2
27                if customfunction.f(x, mid) >= z:
28                    first_true_index = mid
29                    right = mid - 1
30                else:
31                    left = mid + 1
32
33            # Check if first y where f(x, y) >= z gives exactly z
34            if first_true_index != -1 and customfunction.f(x, first_true_index) == z:
35                result.append([x, first_true_index])
36
37        return result
38
1/*
2 * // This is the custom function interface.
3 * // You should not implement it, or speculate about its implementation
4 * class CustomFunction {
5 *     // Returns f(x, y) for any given positive integers x and y.
6 *     // Note that f(x, y) is increasing with respect to both x and y.
7 *     // i.e. f(x, y) < f(x + 1, y), f(x, y) < f(x, y + 1)
8 *     public int f(int x, int y);
9 * };
10 */
11
12class Solution {
13    /**
14     * Find all pairs [x, y] where f(x, y) == z.
15     * For each x, use binary search template to find first y where f(x, y) >= z.
16     * Pattern: false, false, ..., true, true (find first true)
17     */
18    public List<List<Integer>> findSolution(CustomFunction customfunction, int z) {
19        List<List<Integer>> result = new ArrayList<>();
20
21        for (int x = 1; x <= 1000; x++) {
22            // Binary search: find first y where f(x, y) >= z
23            int left = 1;
24            int right = 1000;
25            int firstTrueIndex = -1;
26
27            while (left <= right) {
28                int mid = left + (right - left) / 2;
29
30                if (customfunction.f(x, mid) >= z) {
31                    firstTrueIndex = mid;
32                    right = mid - 1;
33                } else {
34                    left = mid + 1;
35                }
36            }
37
38            // Check if the first y where f(x, y) >= z gives exactly z
39            if (firstTrueIndex != -1 && customfunction.f(x, firstTrueIndex) == z) {
40                result.add(Arrays.asList(x, firstTrueIndex));
41            }
42        }
43
44        return result;
45    }
46}
47
1/*
2 * // This is the custom function interface.
3 * // You should not implement it, or speculate about its implementation
4 * class CustomFunction {
5 * public:
6 *     // Returns f(x, y) for any given positive integers x and y.
7 *     // Note that f(x, y) is increasing with respect to both x and y.
8 *     // i.e. f(x, y) < f(x + 1, y), f(x, y) < f(x, y + 1)
9 *     int f(int x, int y);
10 * };
11 */
12
13class Solution {
14public:
15    /**
16     * Find all pairs [x, y] where f(x, y) == z.
17     * For each x, use binary search template to find first y where f(x, y) >= z.
18     * Pattern: false, false, ..., true, true (find first true)
19     */
20    vector<vector<int>> findSolution(CustomFunction& customfunction, int z) {
21        vector<vector<int>> result;
22
23        for (int x = 1; x <= 1000; ++x) {
24            // Binary search: find first y where f(x, y) >= z
25            int left = 1;
26            int right = 1000;
27            int firstTrueIndex = -1;
28
29            while (left <= right) {
30                int mid = left + (right - left) / 2;
31
32                if (customfunction.f(x, mid) >= z) {
33                    firstTrueIndex = mid;
34                    right = mid - 1;
35                } else {
36                    left = mid + 1;
37                }
38            }
39
40            // Check if the first y where f(x, y) >= z gives exactly z
41            if (firstTrueIndex != -1 && customfunction.f(x, firstTrueIndex) == z) {
42                result.push_back({x, firstTrueIndex});
43            }
44        }
45
46        return result;
47    }
48};
49
1/**
2 * // This is the CustomFunction's API interface.
3 * // You should not implement it, or speculate about its implementation
4 * class CustomFunction {
5 *      f(x: number, y: number): number {}
6 * }
7 */
8
9/**
10 * Find all pairs [x, y] where f(x, y) == z.
11 * For each x, use binary search template to find first y where f(x, y) >= z.
12 * Pattern: false, false, ..., true, true (find first true)
13 *
14 * @param customfunction - The custom function object with method f(x, y)
15 * @param z - The target value to find
16 * @returns Array of [x, y] pairs where f(x, y) = z
17 */
18function findSolution(customfunction: CustomFunction, z: number): number[][] {
19    const results: number[][] = [];
20
21    for (let x = 1; x <= 1000; ++x) {
22        // Binary search: find first y where f(x, y) >= z
23        let left = 1;
24        let right = 1000;
25        let firstTrueIndex = -1;
26
27        while (left <= right) {
28            const mid = Math.floor((left + right) / 2);
29
30            if (customfunction.f(x, mid) >= z) {
31                firstTrueIndex = mid;
32                right = mid - 1;
33            } else {
34                left = mid + 1;
35            }
36        }
37
38        // Check if the first y where f(x, y) >= z gives exactly z
39        if (firstTrueIndex !== -1 && customfunction.f(x, firstTrueIndex) === z) {
40            results.push([x, firstTrueIndex]);
41        }
42    }
43
44    return results;
45}
46

Solution Approach

The implementation uses enumeration combined with binary search to efficiently find all valid pairs.

Step-by-step implementation:

  1. Initialize the result list: Create an empty list ans to store all valid [x, y] pairs.

  2. Enumerate x values: Loop through all possible x values from 1 to z:

    for x in range(1, z + 1):
  3. Binary search for y: For each fixed x, use binary search to find a y value where f(x, y) = z. The code uses Python's bisect_left function with a custom key:

    y = 1 + bisect_left(
        range(1, z + 1), z, key=lambda y: customfunction.f(x, y)
    )

    Here's what happens:

    • range(1, z + 1) creates the search space for y values
    • bisect_left searches for the leftmost position where we could insert z to maintain sorted order
    • key=lambda y: customfunction.f(x, y) tells the binary search to compare based on function values, not the y values themselves
    • We add 1 to the result because bisect_left returns an index (0-based) but our y values start from 1
  4. Check if exact match found: After binary search, verify if the found position gives us exactly z:

    if customfunction.f(x, y) == z:
        ans.append([x, y])

    The binary search finds where f(x, y) >= z. If f(x, y) == z, we have a valid pair. If f(x, y) > z, it means no y value for this x produces exactly z.

  5. Return all pairs: After checking all possible x values, return the collected pairs.

Time Complexity: O(z * log z) - We iterate through z values of x, and for each x, we perform a binary search over at most z values of y.

Space Complexity: O(1) if we don't count the output array, as we only use a constant amount of extra space for variables.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example where we have a hidden function and need to find all pairs (x, y) where f(x, y) = 5.

Suppose the hidden function is f(x, y) = x + y (we don't know this, but it helps visualize the process).

Target: z = 5

We'll iterate through x values from 1 to 5 and use binary search to find corresponding y values:

When x = 1:

  • We binary search for y in range [1, 5] where f(1, y) = 5
  • Binary search process:
    • Check middle: y = 3, f(1, 3) = 4 < 5 (go right)
    • Check y = 4, f(1, 4) = 5 = 5 ✓
  • Found pair: [1, 4]

When x = 2:

  • Binary search for y in range [1, 5] where f(2, y) = 5
  • Binary search process:
    • Check middle: y = 3, f(2, 3) = 5 = 5 ✓
  • Found pair: [2, 3]

When x = 3:

  • Binary search for y in range [1, 5] where f(3, y) = 5
  • Binary search process:
    • Check middle: y = 3, f(3, 3) = 6 > 5 (go left)
    • Check y = 1, f(3, 1) = 4 < 5 (go right)
    • Check y = 2, f(3, 2) = 5 = 5 ✓
  • Found pair: [3, 2]

When x = 4:

  • Binary search for y in range [1, 5] where f(4, y) = 5
  • Binary search process:
    • Check middle: y = 3, f(4, 3) = 7 > 5 (go left)
    • Check y = 1, f(4, 1) = 5 = 5 ✓
  • Found pair: [4, 1]

When x = 5:

  • Binary search for y in range [1, 5] where f(5, y) = 5
  • Binary search process:
    • Check middle: y = 3, f(5, 3) = 8 > 5 (go left)
    • Check y = 1, f(5, 1) = 6 > 5 (go left further)
    • No valid y exists (all values > 5)
  • No pair found

Result: [[1, 4], [2, 3], [3, 2], [4, 1]]

The key insight is that for each fixed x, the function values f(x, 1), f(x, 2), f(x, 3)... form an increasing sequence, allowing us to efficiently binary search for the target value rather than checking every possibility.

Time and Space Complexity

Time Complexity: O(n log n), where n is the value of z.

The outer loop iterates from x = 1 to x = z, which gives us O(n) iterations. For each value of x, we perform a binary search using bisect_left on the range [1, z] to find the appropriate value of y. The binary search operation takes O(log n) time since we're searching through a range of size z. The key function lambda y: customfunction.f(x, y) is called O(log n) times during each binary search. Since we perform this binary search for each of the n values of x, the total time complexity is O(n) × O(log n) = O(n log n).

Space Complexity: O(1) (excluding the output array).

The algorithm uses only a constant amount of extra space for variables x and y. The bisect_left function operates on a range object and uses O(1) additional space for its internal operations. The ans list stores the output, but this is typically not counted in space complexity analysis as it's required for the return value. Therefore, the auxiliary space complexity is O(1).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Using Wrong Binary Search Template Variant

The most critical mistake is using while left < right with right = mid instead of the standard template.

Wrong template:

while left < right:
    mid = (left + right) // 2
    if customfunction.f(x, mid) >= z:
        right = mid  # Wrong pattern
    else:
        left = mid + 1
# Then check if f(x, left) == z

Correct template:

first_true_index = -1
while left <= right:
    mid = (left + right) // 2
    if customfunction.f(x, mid) >= z:
        first_true_index = mid
        right = mid - 1
    else:
        left = mid + 1
# Then check if f(x, first_true_index) == z

2. Inefficient Search Range for y

Pitfall: The code searches for y in the range [1, z] for every x value, but this can be optimized using two pointers.

Optimized solution using two-pointer approach (O(z) instead of O(z log z)):

def findSolution(self, customfunction: 'CustomFunction', z: int) -> List[List[int]]:
    result = []
    x, y = 1, z

    while x <= z and y >= 1:
        current = customfunction.f(x, y)
        if current == z:
            result.append([x, y])
            x += 1
            y -= 1
        elif current < z:
            x += 1
        else:
            y -= 1

    return result

3. Early Termination Opportunity Missed

Pitfall: The solution continues searching even when it's impossible to find more solutions.

Solution: Add an early termination check:

for x in range(1, z + 1):
    # Early termination: if f(x, 1) > z, no valid y exists
    if customfunction.f(x, 1) > z:
        break
    # Binary search for y...

4. Not Checking first_true_index Before Using

Pitfall: Accessing f(x, first_true_index) without checking if first_true_index != -1.

Solution: Always check if first_true_index was set before using it:

if first_true_index != -1 and customfunction.f(x, first_true_index) == z:
    result.append([x, first_true_index])
Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

A heap is a ...?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More