Exemplo n.º 1
0
def with_repeat(arr):  # output is sorted lexicographically
    assert is_sorted(arr)
    n = len(arr)
    yield arr
    while True:
        left = next((i for i in rev_range(n-1) if arr[i] < arr[i+1]), None)  # left: largest index s.t. a[left] < a[left+1]
        if left is None:  # arr is reversely sorted
            return
        right = next(i for i in rev_range(left+1, n) if arr[left] < arr[i])
        # largest index right > left, s.t. a[left] < a[right]. guaranteed to exist as a[left] < a[left+1]
        arr[left], arr[right] = arr[right], arr[left]  # swap arr[left] and arr[right]
        arr[left+1:] = reversed(arr[left+1:])  # arr[left+1:].reverse() reverses on a copy
        yield arr
Exemplo n.º 2
0
 def control(hist):  # O(n^2)
     max_rec, n = 0, len(hist)
     for i, x in enumerate(hist):
         left = next((j for j in rev_range(i) if hist[j] < x), -1)
         right = next((j for j in range(i+1, n) if hist[j] < x), n)
         max_rec = max(max_rec, x * (right - left - 1))
     return max_rec
Exemplo n.º 3
0
def search2(arr):
    """
    Given the price of a stock over n days. Allow at most 2 transactions. Returns the maximum possible profit.
    Observation:
    max_profit = max(for each day i, first sell is <= day i, second buy-in is >= day i)
               = max(for each day i, first sell is on day i, second buy-in is >= day i)
    Note the possibility of selling & buying on day i, which merges two transactions.
    Solution is a 2-pass scan. Time complexity is O(n). Space complexity is O(n).
    :param arr: list[num]
    :return: num
    """
    n = len(arr)
    if n <= 1:
        return 0
    pre_min = arr[0]
    fst = [0]  # fst[i]: maximum profit trading at most, considering arr[:i+1]
    for x in arr[1:]:
        pre_min = min(x, pre_min)
        fst.append(max(fst[-1], x - pre_min))
    profit = 0
    post_max = arr[-1]
    for i in rev_range(n):
        post_max = max(arr[i], post_max)
        profit = max(profit, post_max - arr[i] + fst[i])
    return profit
Exemplo n.º 4
0
def search2(arr):
    """
    Returns a partition, represented as two subsequences a and b, s.t. |sum(a) - sum(b)| is minimised.
    Solution is classic 0-1 knapsack DP. Time complexity is O(n^2). Space complexity is O(s), where s = sum(arr).
    :param arr: list[int], non-negative
    :return: tuple[list[int],list[int]]
    """
    n, s = len(arr), sum(arr)
    if n == 0:
        return [], []
    dp = [None] * (s + 1)
    dp[0] = [0] * n  # bit array for elements in arr
    for i, x in enumerate(arr):
        for j in list(filter_index(lambda x: x is not None, dp)):  # indices must be obtained before modification
            if j + x < s + 1 and dp[j+x] is None:
                dp[j+x] = list(dp[j])
                dp[j+x][i] = 1
    left_ix = next(((i, dp[i]) for i in rev_range(ceil(s/2)) if dp[i] is not None), None)
    right_ix = next(((i, dp[i]) for i in range(ceil(s/2), s+1) if dp[i] is not None), None)
    if left_ix is None:
        selected = right_ix[1]
    elif right_ix is None:
        selected = left_ix[1]
    else:
        selected = min(left_ix, right_ix, key=lambda x: abs(x[0]-s/2))[1]
    left, right = [], []
    for i in range(n):
        if selected[i]:
            left.append(arr[i])
        else:
            right.append(arr[i])
    return left, right
Exemplo n.º 5
0
def insert(arr, start, finish):
    """
    Given a non-overlapping sequence of intervals, denoted as (start, finish) pairs, and sorted by start time,
        inserts a new interval.
    Observation: since intervals are non-overlapping, end times are sorted too.
    Time complexity is O(n). Space complexity is O(n).
    :param arr: list[tuple[num, num]], sorted
    :param start: num, non-negative
    :param finish: num, positive. start < finish
    :return: list[tuple[num, num]]
    """
    n = len(arr)
    left = next((i for i in range(n) if arr[i][1] >= start),
                None)  # last interval that finishes no earlier than the
    # start of the new interval. all intervals in arr[:left] finish before the start of the new interval
    if left is None:  # all intervals have finished when the new interval starts
        return arr + [(start, finish)]
    new_start = min(
        arr[left][0], start
    )  # arr[left] is either fully or partially contained in the new interval
    right = next((i for i in rev_range(n) if arr[i][0] <= finish),
                 None)  # first interval that starts no later than
    # the finish of the new interval. all intervals in arr[right+1:] start after the finish of the new interval
    if right is None:  # no interval has started when the new interval ends
        return [(start, finish)] + arr
    new_end = max(arr[right][1], finish)
    return list(arr[:left]) + [(new_start, new_end)] + list(arr[right + 1:])
Exemplo n.º 6
0
def search2(arr):
    """
    Given the price of a stock over n days. Allow at most 2 transactions. Returns the maximum possible profit.
    Observation:
    max_profit = max(for each day i, first sell is <= day i, second buy-in is >= day i)
               = max(for each day i, first sell is on day i, second buy-in is >= day i)
    Note the possibility of selling & buying on day i, which merges two transactions.
    Solution is a 2-pass scan. Time complexity is O(n). Space complexity is O(n).
    :param arr: list[num]
    :return: num
    """
    n = len(arr)
    if n <= 1:
        return 0
    pre_min = arr[0]
    fst = [0]  # fst[i]: maximum profit trading at most, considering arr[:i+1]
    for x in arr[1:]:
        pre_min = min(x, pre_min)
        fst.append(max(fst[-1], x - pre_min))
    profit = 0
    post_max = arr[-1]
    for i in rev_range(n):
        post_max = max(arr[i], post_max)
        profit = max(profit, post_max - arr[i] + fst[i])
    return profit
Exemplo n.º 7
0
 def control_subarray(arr):  # O(n^3)
     def step(n):
         for win in sliding_window(arr, n):
             s = set(win)
             if len(s) == n and max(s) - min(s) == n - 1:
                 return True
         return False
     return next(i for i in rev_range(len(arr) + 1) if step(i))
Exemplo n.º 8
0
def int_to_arr(x):  # big-endian
    assert x >= 0
    n = floor(log10(x)) + 1  # 999 -> 3; 1000 -> 4
    arr = [None] * n
    for i in rev_range(n):
        arr[i] = x % 10
        x //= 10
    assert x == 0
    return arr
Exemplo n.º 9
0
    def control_subarray(arr):  # O(n^3)
        def step(n):
            for win in sliding_window(arr, n):
                s = set(win)
                if len(s) == n and max(s) - min(s) == n - 1:
                    return True
            return False

        return next(i for i in rev_range(len(arr) + 1) if step(i))
Exemplo n.º 10
0
def spiral(mat):
    m = len(mat)
    if m == 0:
        return
    n = len(mat[0])
    offset = 0
    while True:
        if offset > (min(m, n) - 1) // 2:  # 1 -> 0, 2 -> 0, 3 -> 1, 4 -> 1
            return
        for i in range(offset, n - offset):
            yield mat[offset][i]
        for j in range(offset + 1, m - offset):
            yield mat[j][n-1-offset]
        if offset > min(m, n) / 2 - 1:  # 1 -> -0.5, 2 -> 0, 3 -> 0.5, 4 -> 1
            return
        for i in rev_range(offset, n - offset - 1):
            yield mat[m-1-offset][i]
        for j in rev_range(offset + 1, m - offset - 1):
            yield mat[j][offset]
        offset += 1
Exemplo n.º 11
0
def spiral(mat: Sequence[Sequence[T]]) -> Generator[T, None, None]:
    M = len(mat)
    if M == 0:
        return
    N = len(mat[0])
    offset = 0
    while True:
        if offset > (min(M, N) - 1) // 2:  # 1 -> 0, 2 -> 0, 3 -> 1, 4 -> 1
            return
        for i in range(offset, N - offset):
            yield mat[offset][i]
        for j in range(offset + 1, M - offset):
            yield mat[j][N - 1 - offset]
        if offset > min(M, N) / 2 - 1:  # 1 -> -0.5, 2 -> 0, 3 -> 0.5, 4 -> 1
            return
        for i in rev_range(offset, N - offset - 1):
            yield mat[M - 1 - offset][i]
        for j in rev_range(offset + 1, M - offset - 1):
            yield mat[j][offset]
        offset += 1
Exemplo n.º 12
0
def next_greater_arr(arr):
    n = len(arr)
    pivot = next((i for i in rev_range(n-1) if arr[i] < arr[i+1]), None)
    if pivot is None:
        return None
    min_idx, min_val = pivot+1, arr[pivot+1]  # arr[pivot] < arr[pivot+1] by the definition of pivot
    for i in range(pivot+2, n):  # smallest a[i] s.t. i > pivot and arr[i] > arr[pivot]
        if arr[i] > arr[pivot] and arr[i] < min_val:
            min_idx, min_val = i, arr[i]
    arr[pivot], arr[min_idx] = arr[min_idx], arr[pivot]
    arr[pivot+1:] = sorted(arr[pivot+1:])
    return arr
Exemplo n.º 13
0
 def control(seq):  # O(n^2)
     def is_balanced(sub):
         d = 0
         for x in sub:
             d += 1 if x == '(' else -1
             if d < 0:
                 return False
         return d == 0
     def step(n):
         for xs in sliding_window(seq, n):
             if is_balanced(xs):
                 return True
         return False
     return next((i for i in rev_range(2, len(seq) + 1) if step(i)), 0)
Exemplo n.º 14
0
    def control(seq):  # O(n^2)
        def is_balanced(sub):
            d = 0
            for x in sub:
                d += 1 if x == '(' else -1
                if d < 0:
                    return False
            return d == 0

        def step(n):
            for xs in sliding_window(seq, n):
                if is_balanced(xs):
                    return True
            return False

        return next((i for i in rev_range(2, len(seq) + 1) if step(i)), 0)
Exemplo n.º 15
0
def search2(mat):
    """
    Returns the area of the maximum rectangle whose four corners are all 1s.
    Time complexity is O(n^3). Space complexity is O(1).
    :param mat: list[list[bool]]
    :return: int, non-negative
    """
    if len(mat) == 0 or len(mat[0]) == 0:  # numpy compatibility
        return 0
    m, n = len(mat), len(mat[0])
    max_area = 0
    for top in range(m):
        for bottom in range(top, m):
            left = next((i for i in range(n) if mat[top][i] == mat[bottom][i] == 1), None)
            if left is None:
                continue
            right = next(i for i in rev_range(n) if mat[top][i] == mat[bottom][i] == 1)
            max_area = max(max_area, (right - left + 1) * (bottom - top + 1))
    return max_area
Exemplo n.º 16
0
def search2(mat):
    """
    Returns the area of the maximum rectangle whose four corners are all 1s.
    Time complexity is O(n^3). Space complexity is O(1).
    :param mat: list[list[bool]]
    :return: int, non-negative
    """
    if len(mat) == 0 or len(mat[0]) == 0:  # numpy compatibility
        return 0
    m, n = len(mat), len(mat[0])
    max_area = 0
    for top in range(m):
        for bottom in range(top, m):
            left = next(
                (i for i in range(n) if mat[top][i] == mat[bottom][i] == 1),
                None)
            if left is None:
                continue
            right = next(i for i in rev_range(n)
                         if mat[top][i] == mat[bottom][i] == 1)
            max_area = max(max_area, (right - left + 1) * (bottom - top + 1))
    return max_area
Exemplo n.º 17
0
def search2(arr):
    """
    Returns a partition, represented as two subsequences a and b, s.t. |sum(a) - sum(b)| is minimised.
    Solution is classic 0-1 knapsack DP. Time complexity is O(n^2). Space complexity is O(s), where s = sum(arr).
    :param arr: list[int], non-negative
    :return: tuple[list[int],list[int]]
    """
    n, s = len(arr), sum(arr)
    if n == 0:
        return [], []
    dp = [None] * (s + 1)
    dp[0] = [0] * n  # bit array for elements in arr
    for i, x in enumerate(arr):
        for j in list(filter_index(
                lambda x: x is not None,
                dp)):  # indices must be obtained before modification
            if j + x < s + 1 and dp[j + x] is None:
                dp[j + x] = list(dp[j])
                dp[j + x][i] = 1
    left_ix = next(
        ((i, dp[i]) for i in rev_range(ceil(s / 2)) if dp[i] is not None),
        None)
    right_ix = next(
        ((i, dp[i]) for i in range(ceil(s / 2), s + 1) if dp[i] is not None),
        None)
    if left_ix is None:
        selected = right_ix[1]
    elif right_ix is None:
        selected = left_ix[1]
    else:
        selected = min(left_ix, right_ix, key=lambda x: abs(x[0] - s / 2))[1]
    left, right = [], []
    for i in range(n):
        if selected[i]:
            left.append(arr[i])
        else:
            right.append(arr[i])
    return left, right
Exemplo n.º 18
0
def insert(arr, start, finish):
    """
    Given a non-overlapping sequence of intervals, denoted as (start, finish) pairs, and sorted by start time,
        inserts a new interval.
    Observation: since intervals are non-overlapping, end times are sorted too.
    Time complexity is O(n). Space complexity is O(n).
    :param arr: list[tuple[num, num]], sorted
    :param start: num, non-negative
    :param finish: num, positive. start < finish
    :return: list[tuple[num, num]]
    """
    n = len(arr)
    left = next((i for i in range(n) if arr[i][1] >= start), None)  # last interval that finishes no earlier than the
        # start of the new interval. all intervals in arr[:left] finish before the start of the new interval
    if left is None:  # all intervals have finished when the new interval starts
        return arr + [(start, finish)]
    new_start = min(arr[left][0], start)  # arr[left] is either fully or partially contained in the new interval
    right = next((i for i in rev_range(n) if arr[i][0] <= finish), None)  # first interval that starts no later than
        # the finish of the new interval. all intervals in arr[right+1:] start after the finish of the new interval
    if right is None:  # no interval has started when the new interval ends
        return [(start, finish)] + arr
    new_end = max(arr[right][1], finish)
    return list(arr[:left]) + [(new_start, new_end)] + list(arr[right+1:])
Exemplo n.º 19
0
 def control(arr):  # O(n^2)
     a, n = [], len(arr)
     for i, x in enumerate(arr):
         a.append(next(j for j in rev_range(i, n) if arr[j] >= x))
     return max(x - i for i, x in enumerate(a))
Exemplo n.º 20
0
    lbg = [None] * n
    if n <= 1:  # requires len(arr) >= 2
        return lbg
    i, j = n - 1, n - 1  # arr[i+1:] is scanned, lbg[j+1:] is filled
    while i > 0:
        while i > 0 and arr[i] + gap >= arr[j]:  # slide i leftwards
            i -= 1
        while i <= j and arr[i] + gap < arr[j]:  # slide j leftwards
            lbg[j] = i
            j -= 1
    return lbg


if __name__ == '__main__':
    std_test = {
        ((), 1): [],
        ((2, ), 1): [None],
        ((1, 2), 0): [None, 0],
        ((2, 5, 5, 9), 2): [None, 0, 0, 2]
    }
    for k, v in std_test.items():
        assert search(*k) == v
    from random import randint
    for size in [x for x in range(50) for _ in range(x)]:
        a = sorted(randint(-size, size) for _ in range(size))
        g = randint(0, size)
        assert search(a, g) == [
            next((j for j in rev_range(i) if a[j] + g < a[i]), None)
            for i in range(size)
        ]
Exemplo n.º 21
0
 def heapify(self):
     for i in rev_range(len(self.a)):
         # order is not important, as long as heap property is maintained for all layers
         self.bubble_up(i)
         self.trickle_down(i)
Exemplo n.º 22
0
 def heapify(self):
     for i in rev_range(len(self.a)):
         # order is not important, as long as heap property is maintained for all layers
         self.bubble_up(i)
         self.trickle_down(i)
Exemplo n.º 23
0
 def control(arr):  # O(n^2)
     a, n = [], len(arr)
     for i, x in enumerate(arr):
         a.append(next(j for j in rev_range(i, n) if arr[j] >= x))
     return max(x - i for i, x in enumerate(a))
Exemplo n.º 24
0
 def control_prev(arr):
     n = len(arr)
     r = [] * n
     for i in range(n):
         r.append(next((j for j in rev_range(i) if arr[j] > arr[i]), None))
     return r
Exemplo n.º 25
0
    :param gap: num, non-negative
    :return: list[Optional[int]]
    """
    assert gap >= 0
    assert is_sorted(arr)
    n = len(arr)
    lbg = [None] * n
    if n <= 1:  # requires len(arr) >= 2
        return lbg
    i, j = n-1, n-1  # arr[i+1:] is scanned, lbg[j+1:] is filled
    while i > 0:
        while i > 0 and arr[i] + gap >= arr[j]:  # slide i leftwards
            i -= 1
        while i <= j and arr[i] + gap < arr[j]:  # slide j leftwards
            lbg[j] = i
            j -= 1
    return lbg

if __name__ == '__main__':
    std_test = {((), 1): [],
                ((2,), 1): [None],
                ((1, 2), 0): [None, 0],
                ((2, 5, 5, 9), 2): [None, 0, 0, 2]}
    for k, v in std_test.items():
        assert search(*k) == v
    from random import randint
    for size in [x for x in range(50) for _ in range(x)]:
        a = sorted(randint(-size, size) for _ in range(size))
        g = randint(0, size)
        assert search(a, g) == [next((j for j in rev_range(i) if a[j] + g < a[i]), None) for i in range(size)]