Talentd
FleetCode
Make and follow sheets.Sign in

Maximum Sum of Alternating Subsequence With Distance at Least K solution and explanation

HardArrayDynamic ProgrammingSegment Tree6 min read30.7% acceptanceUpdated 27 Jul 2026
Super Hard💀DP + Segment Tree asked by Leetcode in Weekly Contest 499(Q4,3915)Kumar K [Amazon] · 1:32:59 · 1K viewsWatch on YouTube
3 more videos on this problem
  1. Super Hard💀DP + Segment Tree asked by Leetcode in Weekly Contest 499(Q4,3915)Kumar K [Amazon] · 1:32:59 · 1K views
  2. LeetCode Problem 3915 | Maximum Sum of Alternating Subsequence With Distance at Least KRepovive TV · 27:15 · 385 views
  3. 3915. Maximum Sum of Alternating Subsequence With Distance at Least K (Leetcode Hard)Programming Live with Larry · 48:44 · 127 views
  4. Leetcode Hard #3915. Maximum Sum of Alternating Subsequence With Distance at Least KVicky Jha · 6:49 · 117 views

The problem

You are given an integer array nums of length n and an integer k.

Pick a subsequence with indices 0 <= i1 < i2 < ... < im < n such that:

  • For every 1 <= t < m, it+1 - it >= k.
  • The selected values form a strictly alternating sequence. In other words, either:
    • nums[i1] < nums[i2] > nums[i3] < ..., or
    • nums[i1] > nums[i2] < nums[i3] > ...

A subsequence of length 1 is also considered strictly alternating. The score of a valid subsequence is the sum of its selected values.

Return an integer denoting the maximum possible score of a valid subsequence.

Example 1:

Input: nums = [5,4,2], k = 2

Output: 7

Explanation:

An optimal choice is indices [0, 2], which gives values [5, 2].

  • The distance condition holds because 2 - 0 = 2 >= k.
  • The values are strictly alternating because 5 > 2.

The score is 5 + 2 = 7.

Example 2:

Input: nums = [3,5,4,2,4], k = 1

Output: 14

Explanation:

An optimal choice is indices [0, 1, 3, 4], which gives values [3, 5, 2, 4].

  • The distance condition holds because each pair of consecutive chosen indices differs by at least k = 1.
  • The values are strictly alternating since 3 < 5 > 2 < 4.

The score is 3 + 5 + 2 + 4 = 14.

Example 3:

Input: nums = [5], k = 1

Output: 5

Explanation:

The only valid subsequence is [5]. A subsequence with 1 element is always strictly alternating, so the score is 5.

Constraints:

  • 1 <= n == nums.length <= 105
  • 1 <= nums[i] <= 105
  • 1 <= k <= n

Approach overview

Problem Overview: Given an array, select a subsequence where consecutive chosen indices differ by at least k. The subsequence contributes an alternating sum: first element added, second subtracted, third added, and so on. Your goal is to maximize the resulting value.

Approach 1: Brute Force Enumeration (Exponential Time, O(2^n) time, O(n) space)

Generate every subsequence and filter those where consecutive indices differ by at least k. For each valid subsequence, compute the alternating sum by toggling between addition and subtraction. Track the maximum across all candidates. This method directly models the problem but quickly becomes infeasible as n grows because the number of subsequences doubles at each step. Useful only for validating small test cases or building intuition about the alternating pattern.

Approach 2: Dynamic Programming with Pair Transitions (O(n^2) time, O(n) space)

Define two DP states for each index i. add[i] represents the best alternating sum ending at i where nums[i] is added, and sub[i] represents the best sum ending at i where nums[i] is subtracted. For every i, scan previous indices j such that i - j ≥ k. Transition with add[i] = nums[i] + sub[j] and sub[i] = -nums[i] + add[j]. This enforces both the alternating pattern and the distance constraint. The nested loop makes the runtime quadratic, which may struggle when n is large.

Approach 3: Optimized DP with Prefix Maximum (O(n) time, O(n) space)

The transition only needs the maximum valid previous state where the index difference is at least k. Instead of scanning all previous j, maintain prefix maxima for the best add and sub values whose indices are ≤ i - k. When processing index i, compute add[i] = nums[i] + bestSubPrefix and sub[i] = -nums[i] + bestAddPrefix. After index i becomes eligible (when future indices reach i + k), update the prefix maxima. This converts the quadratic scan into constant-time transitions while still respecting the distance constraint.

The technique is a classic dynamic programming optimization: convert repeated range scans into tracked aggregates. Similar ideas appear in problems that maintain rolling maxima or use monotonic structures in array processing and advanced algorithm design.

Recommended for interviews: Start by describing the O(n^2) DP since it clearly models the alternating transitions and distance rule. Then optimize it using prefix maxima to reach O(n) time. Interviewers typically expect this optimization because it demonstrates recognition of repeated range queries and the ability to compress them into constant-time updates.

Solution

State Definition

Let f[i][0] denote the maximum sum of a valid subsequence ending at index i where the last element is a valley (the next element must be larger to maintain alternation), and f[i][1] denote the maximum sum where the last element is a peak (the next element must be smaller).

Transitions

When transitioning, we enumerate a predecessor index j satisfying j leq i - k:

  • State f[i][0] (valley): transitions from f[j][1], requiring nums[j] > nums[i], i.e., query the maximum f[cdot][1] over the value range (nums[i],\ +infty):

$f[i][0] = nums[i] + max!\left(0,\ max_{\substack{j leq i-k \ nums[j] > nums[i]}} f[j][1]\right)

  • State f[i][1] (peak): transitions from f[j][0], requiring nums[j] < nums[i], i.e., query the maximum f[cdot][0] over the value range [1,\ nums[i]-1]:

f[i][1] = nums[i] + max!\left(0,\ max_{\substack{j leq i-k \ nums[j] < nums[i]}} f[j][0]\right)

The final answer is max_{0 leq i < n}max(f[i][0],\ f[i][1]).

Optimization

The transitions involve dynamic prefix/suffix maximum queries over a value domain, which can be maintained efficiently with two Binary Indexed Trees (BITs):

  • BIT bit_0: indexed by value, maintains the prefix maximum of f[cdot][0], used to query cases where nums[j] < nums[i].
  • BIT bit_1: indexed by M + 1 - val (reversed, where M = max(nums) ), maintains the prefix maximum of f[cdot][1], equivalent to a suffix maximum over the value domain, used to query cases where nums[j] > nums[i].

To ensure only indices j leq i - k participate in transitions, when processing index i, we insert the state of index i - k into the BITs using a sliding pointer.

The time complexity is O(n log M) and the space complexity is O(M), where n is the length of the array and M = max(nums)$.

class FenwickTree:
    def __init__(self, n):
        self.n = n
        self.tree = [0] * (n + 1)

    def update(self, index: int, val: int) -> None:
        while index <= self.n:
            self.tree[index] = max(self.tree[index], val)
            index += index & (-index)  # 往后更新

    def preSum(self, pos):
        # 按照预期的方式求前缀最大值
        ans = 0
        while pos >= 1:
            ans = max(ans, self.tree[pos])
            pos -= pos & (-pos)
        return ans


class Solution:
    def maxAlternatingSum(self, nums: list[int], k: int) -> int:
        stl = sorted(set(nums))  # 将nums中不同的数字进行排序
        rank = {
            v: i + 1 for i, v in enumerate(stl)
        }  # 将nums中的值快速转换成stl中的索引
        fwt0 = FenwickTree(len(stl))
        fwt1 = FenwickTree(len(stl))

        n = len(nums)
        dp = [[0, 0] for _ in range(n)]
        res = nums[0]
        for i in range(n):
            dp[i][0] = dp[i][1] = nums[i]
            if i >= k:
                indx = rank[nums[i]]  # 找到nums[i]在stl中的索引
                dp[i][1] = max(
                    dp[i][1], fwt0.preSum(indx - 1) + nums[i]
                )  # indx-1即表示小于nums[i]的部分
                dp[i][0] = max(
                    dp[i][0], fwt1.preSum(len(stl) - indx) + nums[i]
                )  # len(stl)-indx即表示在倒序列表中大于nums[i]的部分

            if i - k + 1 >= 0:
                indx = rank[nums[i - k + 1]]
                fwt0.update(indx, dp[i - k + 1][0])  # 在正序列表中更新i-k+1位置的值
                fwt1.update(
                    len(stl) - indx + 1, dp[i - k + 1][1]
                )  # 在倒序列表中更新i-k+1位置的值

            res = max(res, dp[i][0], dp[i][1])  # 更新答案

        return res
class Solution {
    public long maxAlternatingSum(int[] nums, int k) {
        long maxSum = 0;
        int n = nums.length;
        int m = Arrays.stream(nums).max().getAsInt();
        long[][] dp = new long[n][2];
        SegmentTree[] sts = new SegmentTree[2];
        for (int j = 0; j < 2; j++) {
            sts[j] = new SegmentTree(m + 1);
        }
        for (int i = 0; i < n; i++) {
            if (i >= k) {
                sts[0].update(nums[i - k], dp[i - k][0]);
                sts[1].update(nums[i - k], dp[i - k][1]);
            }
            dp[i][0] = sts[1].getMax(0, nums[i] - 1) + nums[i];
            dp[i][1] = sts[0].getMax(nums[i] + 1, m) + nums[i];
            maxSum = Math.max(maxSum, Math.max(dp[i][0], dp[i][1]));
        }
        return maxSum;
    }
}

class SegmentTree {
    private int n;
    private long[] tree;

    public SegmentTree(int n) {
        this.n = n;
        this.tree = new long[n * 4];
    }

    public long getMax(int start, int end) {
        return getMax(start, end, 0, 0, n - 1);
    }

    public void update(int index, long value) {
        update(index, value, 0, 0, n - 1);
    }

    private long getMax(int rangeStart, int rangeEnd, int treeIndex, int treeStart, int treeEnd) {
        if (rangeStart > rangeEnd) {
            return 0;
        }
        if (rangeStart == treeStart && rangeEnd == treeEnd) {
            return tree[treeIndex];
        }
        int mid = treeStart + (treeEnd - treeStart) / 2;
        if (rangeEnd <= mid) {
            return getMax(rangeStart, rangeEnd, treeIndex * 2 + 1, treeStart, mid);
        } else if (rangeStart > mid) {
            return getMax(rangeStart, rangeEnd, treeIndex * 2 + 2, mid + 1, treeEnd);
        } else {
            return Math.max(getMax(rangeStart, mid, treeIndex * 2 + 1, treeStart, mid), getMax(mid + 1, rangeEnd, treeIndex * 2 + 2, mid + 1, treeEnd));
        }
    }

    private void update(int rangeIndex, long value, int treeIndex, int start, int end) {
        if (start == end) {
            tree[treeIndex] = value;
            return;
        }
        int mid = start + (end - start) / 2;
        if (rangeIndex <= mid) {
            update(rangeIndex, value, treeIndex * 2 + 1, start, mid);
        } else {
            update(rangeIndex, value, treeIndex * 2 + 2, mid + 1, end);
        }
        tree[treeIndex] = Math.max(tree[treeIndex * 2 + 1], tree[treeIndex * 2 + 2]);
    }
}
class Solution {
public:
    long long maxAlternatingSum(vector<int>& nums, int K) {
        int n = nums.size();

        int idx[n];
        map<int, int> mp;
        for (int x : nums) mp[x] = 1;
        int m = 0;
        for (auto &p : mp) p.second = ++m;
        for (int i = 0; i < n; i++) idx[i] = mp[nums[i]];

        const long long INF = 1e18;
        long long tree[2][m + 1];
        for (int i = 0; i < 2; i++) for (int j = 0; j <= m; j++) tree[i][j] = -INF;


        auto lb = [&](int x) { return x & (-x); };

        auto update = [&](long long *tree, int pos, long long val) {
            for (; pos <= m; pos += lb(pos)) tree[pos] = max(tree[pos], val);
        };

        auto query = [&](long long *tree, int pos) {
            long long ret = -INF;
            for (; pos; pos -= lb(pos)) ret = max(ret, tree[pos]);
            return ret;
        };


        long long ans = 0;
        long long f[n + 1][2];
        for (int i = 0; i <= n; i++) for (int j = 0; j < 2; j++) f[i][j] = -INF;
        for (int i = 1, j = 1; i <= n; i++) {
            while (i - j >= K) {
                update(tree[0], idx[j - 1], f[j][0]);
                update(tree[1], m + 1 - idx[j - 1], f[j][1]);
                j++;
            }
            f[i][0] = max(0LL, query(tree[1], m - idx[i - 1])) + nums[i - 1];
            f[i][1] = max(0LL, query(tree[0], idx[i - 1] - 1)) + nums[i - 1];
            ans = max({ans, f[i][0], f[i][1]});
        }
        return ans;
    }
};
type fenwick []int64

func (f fenwick) update(i int, val int64) {
	for ; i < len(f); i += i & -i {
		f[i] = max(f[i], val)
	}
}

func (f fenwick) preMax(i int) (res int64) {
	for ; i > 0; i &= i - 1 {
		res = max(res, f[i])
	}
	return
}

func maxAlternatingSum(nums []int, k int) (ans int64) {
	sorted := slices.Clone(nums)
	slices.Sort(sorted)
	sorted = slices.Compact(sorted)

	n := len(nums)
	fInc := make([]int64, n)
	fDec := make([]int64, n)

	m := len(sorted)
	inc := make(fenwick, m+1)
	dec := make(fenwick, m+1)

	for i, x := range nums {
		if i >= k {
			j := nums[i-k]
			inc.update(m-j, fInc[i-k])
			dec.update(j+1, fDec[i-k])
		}

		j := sort.SearchInts(sorted, x)
		nums[i] = j

		fInc[i] = dec.preMax(j) + int64(x)
		fDec[i] = inc.preMax(m-1-j) + int64(x)
		ans = max(ans, fInc[i], fDec[i])
	}

	return
}
Try this approach in the editor

Complexity analysis

ApproachTimeSpaceWhen to Use
Brute Force SubsequencesO(2^n)O(n)Only for very small arrays or conceptual understanding
Dynamic Programming with Nested ScanO(n^2)O(n)When constraints are small and implementation simplicity matters
DP with Prefix Maximum OptimizationO(n)O(n)Best general solution for large inputs; avoids repeated scans

Frequently asked questions

Is Maximum Sum of Alternating Subsequence With Distance at Least K easy or hard?

The problem is classified as Hard because it combines two constraints: alternating subsequence sums and a minimum index distance. Recognizing the DP state transitions is manageable, but optimizing the naive O(n^2) approach to O(n) using prefix maxima is the key challenge.

Maximum Sum of Alternating Subsequence With Distance at Least K Python/Java solution

Most implementations use two DP arrays (add and sub) and variables storing the best prefix values. Python solutions typically iterate once through the array while updating prefix maxima. Java and C++ follow the same logic with arrays or long variables to track the best alternating states.

How to solve Maximum Sum of Alternating Subsequence With Distance at Least K in O(n)?

Track two DP arrays: add[i] for subsequences ending at i with a plus operation and sub[i] for those ending with a minus operation. Maintain prefix maximum values for add and sub among indices ≤ i − k. Use these to compute transitions add[i] = nums[i] + bestSub and sub[i] = -nums[i] + bestAdd. Updating prefix maxima as indices become eligible keeps the algorithm linear.

What is the best approach for Maximum Sum of Alternating Subsequence With Distance at Least K?

The most efficient approach uses dynamic programming with prefix maximum optimization. Maintain two states: one where the current element is added and one where it is subtracted. By tracking the best previous values whose indices are at least k away, each transition becomes O(1). This reduces the total complexity to O(n) time with O(n) space.

Is Maximum Sum of Alternating Subsequence With Distance at Least K asked at Google/Amazon/Meta?

Alternating subsequence and constrained subsequence DP problems appear frequently in interviews at companies like Google, Amazon, and Meta. Variants involving index gaps, alternating sums, or prefix optimizations are common because they test dynamic programming fundamentals and optimization of repeated range queries.

What data structure is used in Maximum Sum of Alternating Subsequence With Distance at Least K?

The core solution uses dynamic programming arrays along with prefix maximum tracking. No complex structure is required if you maintain rolling maximum values as indices become valid. In some variations, segment trees or heaps can replace prefix maxima when the valid range is more complex.

What is the time complexity of Maximum Sum of Alternating Subsequence With Distance at Least K?

The optimal solution runs in O(n) time using dynamic programming with prefix maxima. Each index is processed once, and previous valid states are retrieved in constant time. A straightforward DP implementation without optimization takes O(n^2) time because it scans all earlier indices satisfying the distance constraint.

Comments

Now write it yourselfClose this page and solve Maximum Sum of Alternating Subsequence With Distance at Least K in the editor, without looking.
Practise Maximum Sum of Alternating Subsequence With Distance at Least K