PracHub
QuestionsPremiumLearningGuidesCheatsheetNEWCoaches

Quick Overview

This question evaluates understanding of graph modeling and path-optimization in directed weighted graphs, specifically reasoning about chained conversions and maximizing multiplicative path weights.

  • medium
  • Rippling
  • Coding & Algorithms
  • Software Engineer

Convert amounts between multiple currencies

Company: Rippling

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

You are given a set of direct currency exchange rates and a list of queries. Each exchange rate gives you how to convert from one currency to another. - You can assume there are up to \(10^4\) distinct currencies. - You are given `n` direct exchange rates, each described as: `from_currency`, `to_currency`, `rate`, meaning: - 1 unit of `from_currency` can be exchanged for `rate` units of `to_currency`. - Exchange rates are **directed**: if you know `A -> B` at rate `r`, you **cannot** assume you know `B -> A` unless an explicit rate is given. - You can chain exchanges: if you know `A -> B` and `B -> C`, then you can exchange from `A` to `C` via `B`. - You may assume that if a chain exists, the effective rate is the product of rates along the path. You are also given `q` queries. Each query is of the form: - `source_currency`, `target_currency`, `amount`. For each query, compute the **maximum** amount of `target_currency` you can obtain by converting the given `amount` units of `source_currency`, using zero or more intermediate currencies and the available direct exchange rates. - If no conversion path exists from `source_currency` to `target_currency`, return `-1` for that query. - You may assume there are no arbitrage cycles that increase money unboundedly (i.e., no cycles whose product of rates is greater than 1). **Input format (conceptual):** - Integer `n` — number of direct exchange rates. - Next `n` lines: `from_currency to_currency rate` (e.g., `USD EUR 0.9`). - Integer `q` — number of queries. - Next `q` lines: `source_currency target_currency amount`. **Output:** - For each query, output a real number representing the maximum amount of `target_currency` you can obtain, or `-1` if it is impossible. **Task:** Describe an efficient algorithm to answer all queries. - Clearly specify the data structures you will use. - State the time and space complexity in terms of `n` and `q`.

Quick Answer: This question evaluates understanding of graph modeling and path-optimization in directed weighted graphs, specifically reasoning about chained conversions and maximizing multiplicative path weights.

You are given directed currency exchange rates and a list of conversion queries. Each rate `(from_currency, to_currency, rate)` means 1 unit of `from_currency` can be exchanged for `rate` units of `to_currency`. You may chain multiple exchanges, and the effective rate of a path is the product of its edge rates. For each query `(source_currency, target_currency, amount)`, return the maximum amount of `target_currency` obtainable from the given `amount` of `source_currency`. If no conversion path exists, return `-1`. A zero-length conversion is allowed, so converting a currency to itself returns the original amount. You may assume there is no arbitrage cycle whose product is greater than 1.

Constraints

  • 0 <= len(rates) <= 2 * 10^4
  • 0 <= len(queries) <= 10^4
  • There are at most 10^4 distinct currencies in the rate list
  • All exchange rates are positive
  • No directed cycle has a product of rates greater than 1

Examples

Input: ([('USD', 'EUR', 0.9), ('EUR', 'JPY', 130.0), ('USD', 'JPY', 100.0)], [('USD', 'JPY', 2.0), ('JPY', 'USD', 1.0), ('USD', 'USD', 5.0)])

Expected Output: [234.0, -1, 5.0]

Explanation: For USD -> JPY, the best path is USD -> EUR -> JPY with rate 0.9 * 130 = 117, so 2 * 117 = 234. There is no path from JPY to USD. Converting USD to itself uses zero exchanges, so the answer is 5.0.

Input: ([('A', 'B', 2.0), ('A', 'B', 3.0), ('B', 'C', 4.0), ('A', 'C', 10.0)], [('A', 'C', 1.0), ('C', 'C', 2.0)])

Expected Output: [12.0, 2.0]

Explanation: Between the duplicate A -> B edges, the better rate is 3.0. Then A -> B -> C gives 3 * 4 = 12, which beats the direct rate 10.0. Converting C to C returns the original amount.

Input: ([('X', 'Y', 1.5), ('Y', 'Z', 2.0)], [('X', 'Z', 3.0), ('Z', 'X', 1.0), ('Q', 'Q', 7.0), ('Q', 'X', 1.0)])

Expected Output: [9.0, -1, 7.0, -1]

Explanation: X -> Z is possible through Y with total rate 1.5 * 2.0 = 3.0, so 3.0 units become 9.0. There is no path from Z to X. Q -> Q returns 7.0 even though Q is not in the rate list. Q -> X is impossible.

Input: ([('A', 'B', 0.5), ('A', 'C', 2.0), ('C', 'B', 0.4), ('B', 'D', 3.0), ('C', 'D', 0.7)], [('A', 'D', 10.0)])

Expected Output: [24.0]

Explanation: The best path is A -> C -> B -> D with rate 2.0 * 0.4 * 3.0 = 2.4, so 10.0 becomes 24.0. Other paths produce less.

Input: ([], [('USD', 'EUR', 1.0), ('USD', 'USD', 1.0)])

Expected Output: [-1, 1.0]

Explanation: With no exchange rates, converting between different currencies is impossible. A currency can always remain unchanged, so USD -> USD returns 1.0.

Solution

def solution(rates, queries):
    import math
    import heapq

    if not rates:
        return [round(float(amount), 10) if src == dst else -1 for src, dst, amount in queries]

    currency_to_id = {}
    for src, dst, rate in rates:
        if src not in currency_to_id:
            currency_to_id[src] = len(currency_to_id)
        if dst not in currency_to_id:
            currency_to_id[dst] = len(currency_to_id)

    # Keep only the best direct rate for each ordered pair.
    best_edge = {}
    for src, dst, rate in rates:
        u = currency_to_id[src]
        v = currency_to_id[dst]
        w = -math.log(rate)
        key = (u, v)
        if key not in best_edge or w < best_edge[key]:
            best_edge[key] = w

    v_count = len(currency_to_id)
    edges = []
    for (u, v), w in best_edge.items():
        edges.append((u, v, w))

    # Bellman-Ford from an implicit super source connected to every vertex with 0 weight.
    # This is equivalent to initializing all distances to 0.
    h = [0.0] * v_count
    for _ in range(v_count - 1):
        updated = False
        for u, v, w in edges:
            candidate = h[u] + w
            if candidate < h[v] - 1e-15:
                h[v] = candidate
                updated = True
        if not updated:
            break

    # Reweight edges so all weights become non-negative.
    graph = [[] for _ in range(v_count)]
    for u, v, w in edges:
        rw = w + h[u] - h[v]
        if rw < 0 and rw > -1e-12:
            rw = 0.0
        graph[u].append((v, rw))

    answers = [None] * len(queries)
    grouped = {}

    for i, (src, dst, amount) in enumerate(queries):
        amount = float(amount)
        if src == dst:
            answers[i] = round(amount, 10)
        elif src not in currency_to_id or dst not in currency_to_id:
            answers[i] = -1
        else:
            s = currency_to_id[src]
            t = currency_to_id[dst]
            grouped.setdefault(s, []).append((i, t, amount))

    for s, bucket in grouped.items():
        dist = [float('inf')] * v_count
        dist[s] = 0.0
        heap = [(0.0, s)]

        while heap:
            d, u = heapq.heappop(heap)
            if d > dist[u] + 1e-15:
                continue
            for v, w in graph[u]:
                nd = d + w
                if nd + 1e-15 < dist[v]:
                    dist[v] = nd
                    heapq.heappush(heap, (nd, v))

        for idx, t, amount in bucket:
            if math.isinf(dist[t]):
                answers[idx] = -1
            else:
                original_dist = dist[t] - h[s] + h[t]
                result = amount * math.exp(-original_dist)
                answers[idx] = round(result, 10)

    return answers

Time complexity: O(V * n + s * (n + V) log V), where V is the number of distinct currencies, n is the number of direct exchange rates, and s <= q is the number of distinct source currencies appearing in the queries.. Space complexity: O(n + V + q).

Hints

  1. A product along a path becomes a sum if you take logarithms. Maximizing a product can be turned into minimizing a path cost.
  2. After transforming each edge weight to `-log(rate)`, the graph has no negative cycles. Reweight once, then run Dijkstra for each distinct query source.
Last updated: May 14, 2026

Loading coding console...

PracHub

Master your tech interviews with 7,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Design a Driver Payroll System - Rippling (medium)
  • Compare Complete or Partial Hands - Rippling (medium)
  • Implement Food Delivery Billing - Rippling (medium)
  • Implement a balance tracker and interval merger - Rippling (medium)
  • Compute total wages and partial-hour payments - Rippling (medium)