Найти уникальный или комбинацию для данного массива

У меня есть массив чисел размера n, я выбираю все возможные различные возрастающие подпоследовательности массива, для каждой подпоследовательности нахожу побитовое значение ИЛИ и возвращаю их как результат в порядке возрастания.

Пример:

arr= [4,2,4,1], n = 4

Результат

[1,2,4,6]

Объяснение:

All possible distinct increasing subsequences are:
[4]
[2]
[2, 4]
[1]

Bit wise or values are:
[4]
[2]
[2 | 4] = [6]
[1]

So values are [2,4,6, 1]
sorted result is [1,2,4,6]

Другой пример:

arr = [3,2,4,6], n = 4

Результат

[2,3,4,6,7]

Объяснение:

All increasing subsequences and corresponding bitwise or values are:
[3] : 3
[3, 4] : 7
[3, 4, 6] : 7
[3, 6] : 7
[2] : 2
[2, 4] : 6
[2, 4, 6] : 6
[2, 6] : 6
[4] : 4
[4, 6] : 6
[6] : 6

So distinct values are [3,7,2,6,4]
sorted result is [2,3,4,6,7]

Ограничения:

1 <= n <= 10^4
1 <= arr[i] <= 1024

Я решил это, используя грубую силу, вот мой код:

import java.util.*;

public class Main {

    public static void main(String[] args) {
        int[] arr = {3,2,4,6};

        List<List<Integer>> subsequences = findIncreasingSubsequences(arr);

        Set<Integer> set = new TreeSet<>();
        for (List<Integer> subsequence : subsequences) {
            int or = subsequence.get(0);
            for(int i=1; i<subsequence.size(); i++) {
                or |= subsequence.get(i);
            }
            //System.out.println(subsequence + " : "+ or);
            set.add(or);
        }
        System.out.println("Result : " + set);
    }

    public static List<List<Integer>> findIncreasingSubsequences(int[] arr) {
        List<List<Integer>> result = new ArrayList<>();
        List<Integer> current = new ArrayList<>();

        findSubsequences(arr, result, current, 0);

        return result;
    }

    private static void findSubsequences(int[] arr, List<List<Integer>> result, List<Integer> current, int start) {
        if (!current.isEmpty()) {
            result.add(new ArrayList<>(current)); // Add current subsequence to result
        }

        for (int i = start; i < arr.length; i++) {
            if (current.isEmpty() || arr[i] > current.get(current.size() - 1)) {
                current.add(arr[i]);
                findSubsequences(arr, result, current, i + 1);
                current.remove(current.size() - 1);
            }
        }
    }
}

Как решить эту проблему за меньшее время.

Думаю, я не правильно понял утверждение - по крайней мере мои результаты отличаются от ваших примеров. Что вы подразумеваете под «подпоследовательностью»? Разве {2,4,1} не является подпоследовательностью {4,2,4,1}? Потому что для этого побитовое значение или будет равно 7, и вы не включили это в результат вашего примера № 1.

cyberbrain 08.07.2024 23:43

@cyberbrain Это не возрастающая подпоследовательность (относительно значений, а не индексов).

Unmitigated 08.07.2024 23:46

@Unmitigated, спасибо, пропустил это, но во втором примере действительно ли {3,4} является возрастающей подпоследовательностью? Между этими числами во входных данных есть 2, так что для меня это скорее комбинация, чем подпоследовательность? Последовательность будет означать, что все элементы во входном списке расположены рядом - для меня...

cyberbrain 09.07.2024 00:23

@cyberbrain Подпоследовательности не являются смежными. Они отличаются от подмассивов. См. Википедию: «В математике подпоследовательность данной последовательности — это последовательность, которую можно получить из данной последовательности путем удаления некоторых элементов или их отсутствия без изменения порядка остальных элементов».

Unmitigated 09.07.2024 00:23
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
4
149
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я думаю, не обойтись без того, чтобы не попробовать все возможные комбинации. Но, конечно, вы можете удалить множество ветвей, если они не дадут нового результата.

Вот мой код. У меня такой же подход, как у вас, но я на самом деле не рассчитывал все комбинации, а просто передал значение or-ed для этой текущей комбинации. И сокращение выполняется, если принятие следующего элемента не даст вам нового или заданного значения, которое вы видели раньше.

import java.util.*;
 
class Solution {
    void solve(List<Integer> nums, int index, int lastIndex, int taken, int curr, Set<Integer> values) {
        if (taken > 0) {
            values.add(curr);
        }
        if (index >= nums.size()) {
            return;
        }
 
        // Take it.
        if (lastIndex == -1 || nums.get(index) > nums.get(lastIndex)) {
            int newOrValue = curr | nums.get(index);
            if (!values.contains(newOrValue)) {
                solve(nums, index + 1, index, taken + 1, newOrValue, values);
            }
        }
        // Leave it
        solve(nums, index + 1, lastIndex, taken, curr, values);
    }
 
    public List<Integer> getIncreasingSubsequenceOrValues(List<Integer> nums) {
        Set<Integer> values = new HashSet<>();
        solve(nums, 0 /* index */, -1 /* lastIndex */, 0 /* taken */, 0 /* curr */, values);
        return new ArrayList<>(values);
    }
}
 
public class Main {
    static void print(List<Integer> v) {
        for (int el : v) {
            System.out.print(el + " ");
        }
        System.out.println();
    }
 
    static void test(List<Integer> nums) {
        System.out.print("Testing for array: ");
        print(nums);
 
        Solution solution = new Solution();
        List<Integer> result = solution.getIncreasingSubsequenceOrValues(nums);
 
        print(result);
    }
 
    public static void main(String[] args) {
        test(Arrays.asList(4, 2, 4, 1));
        test(Arrays.asList(3, 2, 4, 6));
    }
}

Можно запустить по адресу: https://ideone.com/NmfuVV

Ответ принят как подходящий

Поскольку все элементы массива находятся в диапазоне от 1 до 1024 (210), максимальное значение побитового ИЛИ равно 2047. Эту проблему можно решить в O(2047N) с помощью динамического программирования. Для каждого возможного значения побитового ИЛИ поддерживайте минимально возможное значение, которым может закончиться подпоследовательность с этим побитовым значением ИЛИ (чтобы разрешить как можно больше возрастающих подпоследовательностей).

public static List<Integer> solve(int... nums) {
    final int MAX_VAL = 1024, MAX_OR = MAX_VAL | MAX_VAL - 1;
    var minEndVal = new int[MAX_OR + 1];
    for (int i = 1; i <= MAX_OR; i++) minEndVal[i] = MAX_VAL + 1;
    for (int num : nums)
        for (int i = 0; i < MAX_OR; i++)
            if (minEndVal[i] < num)
                minEndVal[i | num] = Math.min(minEndVal[i | num], num);
    return IntStream.rangeClosed(1, MAX_OR).filter(i -> minEndVal[i] <= MAX_VAL).boxed().toList();
}

Спасибо, не могли бы вы объяснить подробнее if (minEndVal[i] < num) minEndVal[i | num] = Math.min(minEndVal[i | num], num);, а также то, как он учитывает только возрастающие комбинации подпоследовательностей?

Sid 09.07.2024 08:31

@Sid minEndVal[i] хранит минимальное значение (которое было замечено до сих пор), которое может быть последним элементом возрастающей подпоследовательности, побитовое ИЛИ которой равно i. Если minEndVal[i] меньше текущего элемента, то мы можем просто добавить текущее число в конец предыдущей возрастающей подпоследовательности и сформировать более длинную возрастающую подпоследовательность. Нам нужно поддерживать minEndVal[i], обновляя минимальное значение, чтобы позже можно было добавить больше элементов в подпоследовательность.

Unmitigated 09.07.2024 08:44

Другие вопросы по теме