Find the Maximum Sequence Value of Array
Problem
You are given an integer array nums and a positive integer k.
The value of a sequence seq of size 2 * x is defined as:
(seq[0] OR seq[1] OR ... OR seq[x - 1]) XOR (seq[x] OR seq[x + 1] OR ... OR seq[2 * x - 1]).
Return the maximum value of any subsequence of nums having size 2 * k.
Examples
Example 1
Input: nums = [2,6,7], k = 1
Output: 5
Explanation:
The subsequence `[2, 7]` has the maximum value of `2 XOR 7 = 5`.
Example 2
Input: nums = [4,2,5,6,7], k = 2
Output: 2
Explanation:
The subsequence `[4, 5, 6, 7]` has the maximum value of `(4 OR 5) XOR (6 OR 7)
= 2`.
Constraints
2 <= nums.length <= 4001 <= nums[i] < 271 <= k <= nums.length / 2
Solution
Method 1 – Naive Brute Force
Intuition
The simplest way is to try every possible subsequence of length 2 * k (keeping original order), compute the bitwise OR of the first k elements and the OR of the last k elements, then take the XOR of those two ORs. Because k is small and n is at most 400, this brute-force enumerative approach is easy to implement and straightforward to reason about.
Approach
- Enumerate all combinations of
2 * kindices from0..n-1in increasing order (these represent subsequences and preserve the original order). - For each chosen index tuple
idxsof size2 * k, computeleft_or = nums[idxs[0]] | nums[idxs[1]] | ... | nums[idxs[k-1]]andright_or = nums[idxs[k]] | ... | nums[idxs[2*k-1]]. - Compute
value = left_or ^ right_orand track the maximum over all choices. - Return the maximum value after trying all combinations.
This method is intentionally straightforward and serves as a correctness baseline; it does no advanced pruning or DP.
Complexity
- ⏰ Time complexity:
O(C(n, 2*k) * k)– We enumerate every combination of2*kindices (there areC(n,2k)of them) and for each we doO(k)work to compute the two ORs. - 🧺 Space complexity:
O(2*k)– We store the current combination of indices and a few scalars while computing ORs.
Method 2 – Memoized (Prefix/Suffix DP)
Intuition
We can avoid enumerating all C(n,2k) subsequences by precomputing, for every prefix and every possible OR value, whether it's possible to pick exactly t elements from that prefix that produce that OR. Do the same for suffixes (or equivalently reverse the array and compute the same DP). Then, for each split point between prefix and suffix (i.e., choose how many original elements belong to the prefix), combine possible left OR values (from prefix DP) with possible right OR values (from suffix DP) and maximize their XOR. This trades combinatorial enumeration for DP over the OR-value space, which is small (W = 128) because numbers are small (<= 127).
Approach
- Let
Wbe the number of possible OR-values (we useW = 128since nums[i] < 128). - Build
dp_prefix[i][t]= set of OR-values achievable by picking exactlytelements from the firstielements ofnums(0 <= i <= n). - Build
dp_suffixby reversingnumsand computing the same DP;dp_suffix[s][t]then represents achievable ORs usingtelements from the lastselements of the original array. - For every split
iwhere the prefix uses the firstielements and the suffix uses the lastn - ielements, check allor1 in dp_prefix[i][k]andor2 in dp_suffix[n - i][k]and update answer withor1 ^ or2. - Return the maximum XOR found.
This DP is efficient because W is small (128), so iterating over OR-values is cheap. We use sets for OR-values in languages where convenient and boolean 3D arrays where sets are less convenient.
State Definition
dp_prefix[i][t]— the set (or boolean mask) of OR-values reachable by selecting exactlytelements from the prefix of lengthi.
Code
C++
class Solution {
public:
int maxValue(vector<int>& nums, int k) {
int n = nums.size();
const int W = 128; // possible OR values: 0..127
auto build = [&](const vector<int>& a) {
int m = a.size();
// dp[i][t][v] : using first i elements, can we pick t elements with OR == v?
vector<vector<vector<char>>> dp(m + 1, vector<vector<char>>(k + 1, vector<char>(W, 0)));
dp[0][0][0] = 1;
for (int i = 0; i < m; ++i) {
for (int t = 0; t <= k; ++t) {
for (int v = 0; v < W; ++v) {
if (!dp[i][t][v]) continue;
// not take
dp[i + 1][t][v] = 1;
// take
if (t < k) dp[i + 1][t + 1][v | a[i]] = 1;
}
}
}
return dp;
};
auto dp1 = build(nums);
reverse(nums.begin(), nums.end());
auto dp2 = build(nums); // dp2 on reversed array
int ans = 0;
for (int i = k; i <= n - k; ++i) {
for (int v1 = 0; v1 < W; ++v1) {
if (!dp1[i][k][v1]) continue;
for (int v2 = 0; v2 < W; ++v2) {
if (!dp2[n - i][k][v2]) continue;
ans = max(ans, v1 ^ v2);
}
}
}
return ans;
}
};
Go
package main
// Helper: compute dp_prefix as slice of slice of maps
func buildDP(a []int, k int) [][][]bool {
m := len(a)
W := 128
dp := make([][][]bool, m+1)
for i := 0; i <= m; i++ {
dp[i] = make([][]bool, k+1)
for t := 0; t <= k; t++ {
dp[i][t] = make([]bool, W)
}
}
dp[0][0][0] = true
for i := 0; i < m; i++ {
for t := 0; t <= k; t++ {
for v := 0; v < W; v++ {
if !dp[i][t][v] {
continue
}
dp[i+1][t][v] = true
if t < k {
dp[i+1][t+1][v | a[i]] = true
}
}
}
}
return dp
}
func maxValue(nums []int, k int) int {
n := len(nums)
dp1 := buildDP(nums, k)
// reverse
for i, j := 0, n-1; i < j; i, j = i+1, j-1 {
nums[i], nums[j] = nums[j], nums[i]
}
dp2 := buildDP(nums, k)
W := 128
ans := 0
for i := k; i <= n-k; i++ {
for v1 := 0; v1 < W; v1++ {
if !dp1[i][k][v1] {
continue
}
for v2 := 0; v2 < W; v2++ {
if !dp2[n-i][k][v2] {
continue
}
x := v1 ^ v2
if x > ans {
ans = x
}
}
}
}
return ans
}
Java
import java.util.*;
class Solution {
public int maxValue(int[] nums, int k) {
int n = nums.length;
final int W = 128;
boolean[][][] dp1 = buildDP(nums, k, W);
// reverse array for suffix DP
reverse(nums);
boolean[][][] dp2 = buildDP(nums, k, W);
int ans = 0;
for (int i = k; i <= n - k; ++i) {
for (int v1 = 0; v1 < W; ++v1) {
if (!dp1[i][k][v1]) continue;
for (int v2 = 0; v2 < W; ++v2) {
if (!dp2[n - i][k][v2]) continue;
ans = Math.max(ans, v1 ^ v2);
}
}
}
return ans;
}
private boolean[][][] buildDP(int[] a, int k, int W) {
int m = a.length;
boolean[][][] dp = new boolean[m + 1][k + 1][W];
dp[0][0][0] = true;
for (int i = 0; i < m; ++i) {
for (int t = 0; t <= k; ++t) {
for (int v = 0; v < W; ++v) {
if (!dp[i][t][v]) continue;
dp[i + 1][t][v] = true;
if (t < k) dp[i + 1][t + 1][v | a[i]] = true;
}
}
}
return dp;
}
private void reverse(int[] a) {
int i = 0, j = a.length - 1;
while (i < j) {
int tmp = a[i]; a[i] = a[j]; a[j] = tmp;
i++; j--;
}
}
}
Kotlin
class Solution {
fun maxValue(nums: IntArray, k: Int): Int {
val n = nums.size
val W = 128
val dp1 = buildDP(nums.copyOf(), k, W)
nums.reverse()
val dp2 = buildDP(nums, k, W)
var ans = 0
for (i in k..n - k) {
for (v1 in 0 until W) {
if (!dp1[i][k][v1]) continue
for (v2 in 0 until W) {
if (!dp2[n - i][k][v2]) continue
ans = maxOf(ans, v1 xor v2)
}
}
}
return ans
}
private fun buildDP(a: IntArray, k: Int, W: Int): Array<Array<BooleanArray>> {
val m = a.size
val dp = Array(m + 1) { Array(k + 1) { BooleanArray(W) } }
dp[0][0][0] = true
for (i in 0 until m) {
for (t in 0..k) {
for (v in 0 until W) {
if (!dp[i][t][v]) continue
dp[i + 1][t][v] = true
if (t < k) dp[i + 1][t + 1][v or a[i]] = true
}
}
}
return dp
}
}
Python
from typing import List
class Solution:
def maxValue(self, nums: List[int], k: int) -> int:
n = len(nums)
W = 128
def build(a: List[int]):
m = len(a)
# dp[i][t] = set of OR-values possible using first i elements picking t elements
dp = [ [set() for _ in range(k+1)] for _ in range(m+1) ]
dp[0][0].add(0)
for i in range(m):
for t in range(k+1):
for v in dp[i][t]:
dp[i+1][t].add(v)
if t < k:
dp[i+1][t+1].add(v | a[i])
return dp
dp1 = build(nums)
nums.reverse()
dp2 = build(nums)
ans = 0
for i in range(k, n - k + 1):
left_set = dp1[i][k]
right_set = dp2[n - i][k]
if not left_set or not right_set:
continue
for v1 in left_set:
for v2 in right_set:
ans = max(ans, v1 ^ v2)
return ans
Rust
impl Solution {
pub fn max_value(nums: Vec<i32>, k: i32) -> i32 {
let n = nums.len();
let k = k as usize;
let w = 128usize;
fn build(a: &Vec<i32>, k: usize, w: usize) -> Vec<Vec<Vec<u8>>> {
let m = a.len();
let mut dp = vec![vec![vec![0u8; w]; k+1]; m+1];
dp[0][0][0] = 1;
for i in 0..m {
for t in 0..=k {
for v in 0..w {
if dp[i][t][v] == 0 { continue; }
dp[i+1][t][v] = 1;
if t < k {
let nv = v | (a[i] as usize);
dp[i+1][t+1][nv] = 1;
}
}
}
}
dp
}
let mut a = nums.clone();
let dp1 = build(&a, k, w);
a.reverse();
let dp2 = build(&a, k, w);
let mut ans = 0i32;
for i in k..=n - k {
for v1 in 0..w {
if dp1[i][k][v1] == 0 { continue; }
for v2 in 0..w {
if dp2[n - i][k][v2] == 0 { continue; }
ans = ans.max((v1 ^ v2) as i32);
}
}
}
ans
}
}
TypeScript
function maxValue(nums: number[], k: number): number {
const n = nums.length;
const W = 128;
function build(a: number[]) {
const m = a.length;
const dp: boolean[][][] = new Array(m+1);
for (let i = 0; i <= m; ++i) {
dp[i] = new Array(k+1);
for (let t = 0; t <= k; ++t) dp[i][t] = new Array(W).fill(false);
}
dp[0][0][0] = true;
for (let i = 0; i < m; ++i) {
for (let t = 0; t <= k; ++t) {
for (let v = 0; v < W; ++v) {
if (!dp[i][t][v]) continue;
dp[i+1][t][v] = true;
if (t < k) dp[i+1][t+1][v | a[i]] = true;
}
}
}
return dp;
}
const dp1 = build(nums.slice());
nums.reverse();
const dp2 = build(nums.slice());
let ans = 0;
for (let i = k; i <= n - k; ++i) {
for (let v1 = 0; v1 < W; ++v1) {
if (!dp1[i][k][v1]) continue;
for (let v2 = 0; v2 < W; ++v2) {
if (!dp2[n - i][k][v2]) continue;
ans = Math.max(ans, v1 ^ v2);
}
}
}
return ans;
}
Complexity
- ⏰ Time complexity:
O(n * k * W + n * W^2)– Building the prefix/suffix DP costsO(n * k * W)and combining prefix/suffix OR-values costsO(n * W^2)whereWis the OR-value range (hereW = 128). - 🧺 Space complexity:
O(n * k * W)– The prefix and suffix DP tables each storeO(n * k * W)boolean states.
Method 3 – Bottom-up DP (Iterative, with rolling updates)
Intuition
The bottom-up DP implements the same reachable-OR idea as Method 2 but builds the DP iteratively from left to right (prefixes) and right to left (suffixes). By using iterative updates (rolling from i to i+1) we make the transition explicit and can optionally optimize memory when full per-index snapshots are not required.
Approach
- Initialize
dp_prefix[0][0] = {0}(only OR 0 with 0 picks). Iteratively computedp_prefix[i+1]fromdp_prefix[i]by adding the option to take or skipnums[i]. - Similarly compute
dp_suffixby iterating from the end of the array backward. - Store for each prefix-length
ithe boolean maskdp_prefix[i][t][v]indicating if ORvis achievable withtpicks. Do the same for suffixes. - Combine prefix and suffix OR-values for each valid split
i(whereielements are available to the left andn-ito the right) and maximizev1 ^ v2over allv1 in dp_prefix[i][k]andv2 in dp_suffix[n-i][k].
This method is equivalent in semantics to Method 2 but written as an explicit iterative DP. It also makes it straightforward to replace the full dp_prefix array with a rolling window if you only need the previous state.
State Definition
dp_prefix[i][t][v]— boolean reachable state: using firstielements, can we pick exactlytelements with combined OR equal tov.
Code
C++
class Solution {
public:
int maxValue(vector<int>& nums, int k) {
int n = nums.size();
const int W = 128;
// build prefix DP iteratively
vector<vector<vector<char>>> dp_pref(n + 1, vector<vector<char>>(k + 1, vector<char>(W, 0)));
dp_pref[0][0][0] = 1;
for (int i = 0; i < n; ++i) {
for (int t = 0; t <= k; ++t) {
for (int v = 0; v < W; ++v) {
if (!dp_pref[i][t][v]) continue;
// skip nums[i]
dp_pref[i+1][t][v] = 1;
// take nums[i]
if (t < k) dp_pref[i+1][t+1][v | nums[i]] = 1;
}
}
}
// build suffix DP iteratively from right to left
vector<vector<vector<char>>> dp_suf(n + 1, vector<vector<char>>(k + 1, vector<char>(W, 0)));
dp_suf[0][0][0] = 1;
for (int i = n-1; i >= 0; --i) {
int idx = n - 1 - i; // number of elements processed in suffix so far
for (int t = 0; t <= k; ++t) {
for (int v = 0; v < W; ++v) {
if (!dp_suf[idx][t][v]) continue;
// skip nums[i]
dp_suf[idx+1][t][v] = 1;
// take nums[i]
if (t < k) dp_suf[idx+1][t+1][v | nums[i]] = 1;
}
}
}
int ans = 0;
for (int i = k; i <= n - k; ++i) {
for (int v1 = 0; v1 < W; ++v1) {
if (!dp_pref[i][k][v1]) continue;
for (int v2 = 0; v2 < W; ++v2) {
if (!dp_suf[n - i][k][v2]) continue;
ans = max(ans, v1 ^ v2);
}
}
}
return ans;
}
};
Go
package main
func maxValue(nums []int, k int) int {
n := len(nums)
W := 128
dpPref := make([][][]bool, n+1)
for i := 0; i <= n; i++ {
dpPref[i] = make([][]bool, k+1)
for t := 0; t <= k; t++ {
dpPref[i][t] = make([]bool, W)
}
}
dpPref[0][0][0] = true
for i := 0; i < n; i++ {
for t := 0; t <= k; t++ {
for v := 0; v < W; v++ {
if !dpPref[i][t][v] { continue }
dpPref[i+1][t][v] = true
if t < k {
dpPref[i+1][t+1][v | nums[i]] = true
}
}
}
}
dpSuf := make([][][]bool, n+1)
for i := 0; i <= n; i++ {
dpSuf[i] = make([][]bool, k+1)
for t := 0; t <= k; t++ {
dpSuf[i][t] = make([]bool, W)
}
}
dpSuf[0][0][0] = true
// build from right to left
for i := n-1; i >= 0; i-- {
idx := n-1-i
for t := 0; t <= k; t++ {
for v := 0; v < W; v++ {
if !dpSuf[idx][t][v] { continue }
dpSuf[idx+1][t][v] = true
if t < k {
dpSuf[idx+1][t+1][v | nums[i]] = true
}
}
}
}
ans := 0
for i := k; i <= n-k; i++ {
for v1 := 0; v1 < W; v1++ {
if !dpPref[i][k][v1] { continue }
for v2 := 0; v2 < W; v2++ {
if !dpSuf[n-i][k][v2] { continue }
x := v1 ^ v2
if x > ans { ans = x }
}
}
}
return ans
}
Java
import java.util.*;
class Solution {
public int maxValue(int[] nums, int k) {
int n = nums.length;
final int W = 128;
boolean[][][] dpPref = new boolean[n+1][k+1][W];
dpPref[0][0][0] = true;
for (int i = 0; i < n; ++i) {
for (int t = 0; t <= k; ++t) {
for (int v = 0; v < W; ++v) {
if (!dpPref[i][t][v]) continue;
dpPref[i+1][t][v] = true;
if (t < k) dpPref[i+1][t+1][v | nums[i]] = true;
}
}
}
boolean[][][] dpSuf = new boolean[n+1][k+1][W];
dpSuf[0][0][0] = true;
for (int i = n-1; i >= 0; --i) {
int idx = n-1-i;
for (int t = 0; t <= k; ++t) {
for (int v = 0; v < W; ++v) {
if (!dpSuf[idx][t][v]) continue;
dpSuf[idx+1][t][v] = true;
if (t < k) dpSuf[idx+1][t+1][v | nums[i]] = true;
}
}
}
int ans = 0;
for (int i = k; i <= n - k; ++i) {
for (int v1 = 0; v1 < W; ++v1) {
if (!dpPref[i][k][v1]) continue;
for (int v2 = 0; v2 < W; ++v2) {
if (!dpSuf[n - i][k][v2]) continue;
ans = Math.max(ans, v1 ^ v2);
}
}
}
return ans;
}
}
Kotlin
class Solution {
fun maxValue(nums: IntArray, k: Int): Int {
val n = nums.size
val W = 128
val dpPref = Array(n+1) { Array(k+1) { BooleanArray(W) } }
dpPref[0][0][0] = true
for (i in 0 until n) {
for (t in 0..k) {
for (v in 0 until W) {
if (!dpPref[i][t][v]) continue
dpPref[i+1][t][v] = true
if (t < k) dpPref[i+1][t+1][v or nums[i]] = true
}
}
}
val dpSuf = Array(n+1) { Array(k+1) { BooleanArray(W) } }
dpSuf[0][0][0] = true
for (i in n-1 downTo 0) {
val idx = n-1-i
for (t in 0..k) {
for (v in 0 until W) {
if (!dpSuf[idx][t][v]) continue
dpSuf[idx+1][t][v] = true
if (t < k) dpSuf[idx+1][t+1][v or nums[i]] = true
}
}
}
var ans = 0
for (i in k..n-k) {
for (v1 in 0 until W) {
if (!dpPref[i][k][v1]) continue
for (v2 in 0 until W) {
if (!dpSuf[n - i][k][v2]) continue
ans = maxOf(ans, v1 xor v2)
}
}
}
return ans
}
}
Python
from typing import List
class Solution:
def maxValue(self, nums: List[int], k: int) -> int:
n = len(nums)
W = 128
dp_pref = [ [set() for _ in range(k+1)] for _ in range(n+1) ]
dp_pref[0][0].add(0)
for i in range(n):
for t in range(k+1):
for v in dp_pref[i][t]:
dp_pref[i+1][t].add(v)
if t < k:
dp_pref[i+1][t+1].add(v | nums[i])
dp_suf = [ [set() for _ in range(k+1)] for _ in range(n+1) ]
dp_suf[0][0].add(0)
for i in range(n-1, -1, -1):
idx = n-1-i
for t in range(k+1):
for v in dp_suf[idx][t]:
dp_suf[idx+1][t].add(v)
if t < k:
dp_suf[idx+1][t+1].add(v | nums[i])
ans = 0
for i in range(k, n - k + 1):
left = dp_pref[i][k]
right = dp_suf[n - i][k]
if not left or not right:
continue
for v1 in left:
for v2 in right:
ans = max(ans, v1 ^ v2)
return ans
Rust
impl Solution {
pub fn max_value(nums: Vec<i32>, k: i32) -> i32 {
let n = nums.len();
let k = k as usize;
let w = 128usize;
let mut dp_pref: Vec<Vec<Vec<u8>>> = vec![vec![vec![0u8; w]; k+1]; n+1];
dp_pref[0][0][0] = 1;
for i in 0..n {
for t in 0..=k {
for v in 0..w {
if dp_pref[i][t][v] == 0 { continue; }
dp_pref[i+1][t][v] = 1;
if t < k {
let nv = v | (nums[i] as usize);
dp_pref[i+1][t+1][nv] = 1;
}
}
}
}
let mut dp_suf: Vec<Vec<Vec<u8>>> = vec![vec![vec![0u8; w]; k+1]; n+1];
dp_suf[0][0][0] = 1;
for i in (0..n).rev() {
let idx = n-1-i;
for t in 0..=k {
for v in 0..w {
if dp_suf[idx][t][v] == 0 { continue; }
dp_suf[idx+1][t][v] = 1;
if t < k {
let nv = v | (nums[i] as usize);
dp_suf[idx+1][t+1][nv] = 1;
}
}
}
}
let mut ans = 0i32;
for i in k..=n - k {
for v1 in 0..w {
if dp_pref[i][k][v1] == 0 { continue; }
for v2 in 0..w {
if dp_suf[n - i][k][v2] == 0 { continue; }
ans = ans.max((v1 ^ v2) as i32);
}
}
}
ans
}
}
TypeScript
function maxValue(nums: number[], k: number): number {
const n = nums.length;
const W = 128;
const dpPref: boolean[][][] = new Array(n+1);
for (let i = 0; i <= n; ++i) {
dpPref[i] = new Array(k+1);
for (let t = 0; t <= k; ++t) dpPref[i][t] = new Array(W).fill(false);
}
dpPref[0][0][0] = true;
for (let i = 0; i < n; ++i) {
for (let t = 0; t <= k; ++t) {
for (let v = 0; v < W; ++v) {
if (!dpPref[i][t][v]) continue;
dpPref[i+1][t][v] = true;
if (t < k) dpPref[i+1][t+1][v | nums[i]] = true;
}
}
}
const dpSuf: boolean[][][] = new Array(n+1);
for (let i = 0; i <= n; ++i) {
dpSuf[i] = new Array(k+1);
for (let t = 0; t <= k; ++t) dpSuf[i][t] = new Array(W).fill(false);
}
dpSuf[0][0][0] = true;
for (let i = n - 1; i >= 0; --i) {
const idx = n - 1 - i;
for (let t = 0; t <= k; ++t) {
for (let v = 0; v < W; ++v) {
if (!dpSuf[idx][t][v]) continue;
dpSuf[idx+1][t][v] = true;
if (t < k) dpSuf[idx+1][t+1][v | nums[i]] = true;
}
}
}
let ans = 0;
for (let i = k; i <= n - k; ++i) {
for (let v1 = 0; v1 < W; ++v1) {
if (!dpPref[i][k][v1]) continue;
for (let v2 = 0; v2 < W; ++v2) {
if (!dpSuf[n - i][k][v2]) continue;
ans = Math.max(ans, v1 ^ v2);
}
}
}
return ans;
}
Complexity
- ⏰ Time complexity:
O(n * k * W + n * W^2)– Iteratively building prefix and suffix DP tables costsO(n * k * W)and combining OR-values across splits costsO(n * W^2). - 🧺 Space complexity:
O(n * k * W)– We store boolean DP tables for prefixes and suffixes in this straightforward bottom-up implementation.