Количество всех возрастающих подпоследовательностей в данной последовательности? - PullRequest
15 голосов
/ 09 января 2011

Возможно, вы слышали об известной проблеме поиска самой длинной подпоследовательности . Оптимальный алгоритм имеет O(n*log(n)) сложность.

Я думал о проблеме поиска всех возрастающих подпоследовательностей в данной последовательности. Я нашел решение для проблемы, где нам нужно найти ряд увеличивающихся подпоследовательностей длины k , которые имеют сложность O(n*k*log(n)) (где n - длина последовательности).

Конечно, этот алгоритм можно использовать для моей задачи, но тогда, я думаю, решение имеет сложность O(n*k*log(n)*n) = O(n^2*k*log(n)). Я думаю, что должно быть лучшее (я имею в виду - более быстрое) решение, но я пока не знаю такого.

Если вы знаете, как решить проблему нахождения всех возрастающих подпоследовательностей в данной последовательности за оптимальное время / сложность (в этом случае, оптимальное = лучше, чем O(n^2*k*log(n))), пожалуйста, дайте мне знать об этом.

В конце концов: эта проблема не домашняя работа. В моей лекции упоминалась проблема самой длинной увеличивающейся подпоследовательности, и я начал думать об общей идее всех возрастающих подпоследовательностей в данной последовательности.

Ответы [ 6 ]

12 голосов
/ 09 января 2011

Я не знаю, является ли это оптимальным - возможно, нет, но вот решение DP в O(n^2).

Пусть dp[i] = number of increasing subsequences with i as the last element

for i = 1 to n do
    dp[i] = 1
    for j = 1 to i - 1 do
        if input[j] < input[i] then
            dp[i] = dp[i] + dp[j] // we can just append input[i] to every subsequence ending with j

Тогда это просто вопроссуммирования всех записей в dp

6 голосов
/ 09 января 2011

Вы можете вычислить количество возрастающих подпоследовательностей за O (n log n) следующим образом.

Напомним алгоритм для длины самой длинной увеличивающейся подпоследовательности:

Для каждого элемента вычислите элемент предшественника среди предыдущих элементов и добавьте один к этой длине.

Этот алгоритм работает наивно за O (n ^ 2) и работает за O (n log n) (или даже лучше, в случае целых чисел), если вы вычисляете предшественник, используя структуру данных, подобную сбалансированному двоичному дерево поиска (BST) (или что-то более продвинутое, например, дерево Ван Эмда Боаса для целых чисел).

Чтобы изменить этот алгоритм для вычисления количества последовательностей, сохраните в BST в каждом узле количество последовательностей, заканчивающихся на этом элементе. При обработке следующего элемента в списке вы просто ищете предшественника, подсчитываете количество последовательностей, оканчивающихся на элементе, меньше, чем элемент, обрабатываемый в данный момент (за O (log n)), и сохраняете результат в BST вместе с текущим элементом. Наконец, вы суммируете результаты для каждого элемента в дереве, чтобы получить результат.

В качестве предостережения обратите внимание, что число увеличивающихся последовательностей может быть очень большим, так что арифметика больше не занимает O (1) времени на операцию. Это необходимо учитывать.

Psuedocode:

ret = 0
T = empty_augmented_bst() // with an integer field in addition to the key
for x int X:

  // sum of auxiliary fields of keys less than x
  // computed in O(log n) time using augmented BSTs
  count = 1 + T.sum_less(x)

  T.insert(x, 1 + count) // sets x's auxiliary field to 1 + count
  ret += count // keep track of return value

return ret
3 голосов
/ 08 июля 2012

Я предполагаю, что без потери обобщения вход A [0 .. (n-1)] состоит из всех целых чисел в {0, 1, ..., n-1}.

ПустьDP [i] = количество возрастающих подпоследовательностей, оканчивающихся на A [i].

У нас есть повторение:

DP[i] = 1 + \sum_{j < i, A[j] < A[i]} DP[j]

Чтобы вычислить DP [i], нам нужно только вычислить DP [j] для всех j, где A [j] A [i].

Проблема сводится квычисление суммы DP [0] в DP [i-1].Предположим, что мы уже вычислили DP [0] для DP [i-1], мы можем вычислить DP [i] в ​​O (log n), используя дерево Фенвика.

Окончательный ответ - DP [0]+ DP [1] + ... DP [n-1].Алгоритм работает в O (n log n).

1 голос
/ 07 февраля 2015

Это решение O (nklogn) , где n - длина входного массива, а k - размер увеличивающихся подпоследовательностей.Он основан на решении , упомянутом в вопросе .

vector<int> values, массив длины n , который нужно искать для увеличения подпоследовательностей.

vector<int> temp(n); // Array for sorting
map<int, int> mapIndex; // This will translate from the value in index to the 1-based count of values less than it

partial_sort_copy(values.cbegin(), values.cend(), temp.begin(), temp.end());

for(auto i = 0; i < n; ++i){
    mapIndex.insert(make_pair(temp[i], i + 1)); // insert will only allow each number to be added to the map the first time
}

mapIndex теперь содержит рейтинг всех чисел в values.

vector<vector<int>> binaryIndexTree(k, vector<int>(n)); // A 2D binary index tree with depth k
auto result = 0;

for(auto it = values.cbegin(); it != values.cend(); ++it){
    auto rank = mapIndex[*it];
    auto value = 1; // Number of sequences to be added to this rank and all subsequent ranks
    update(rank, value, binaryIndexTree[0]); // Populate the binary index tree for sub-sequences of length 1

    for(auto i = 1; i < k; ++i){ // Itterate over all sub-sequence lengths 2 - k
        value = getValue(rank - 1, binaryIndexTree[i - 1]); // Retrieve all possible shorter sub-sequences of lesser or equal rank
        update(rank, value, binaryIndexTree[i]); // Update the binary index tree for sub sequences of this length
    }
    result += value; // Add the possible sub-sequences of length k for this rank
}

После размещения всех n элементов values во всех k размеры binaryIndexTree.value s, собранные в result, представляют общее количество увеличивающихся подпоследовательностей длины k .

Функции дерева двоичных индексов, используемые для получения этого результата:

void update(int rank, int increment, vector<int>& binaryIndexTree)
{
    while (rank < binaryIndexTree.size()) { // Increment the current rank and all higher ranks
        binaryIndexTree[rank - 1] += increment;
        rank += (rank & -rank);
    }
}

int getValue(int rank, const vector<int>& binaryIndexTree)
{
    auto result = 0;
    while (rank > 0) { // Search the current rank and all lower ranks
        result += binaryIndexTree[rank - 1]; // Sum any value found into result
        rank -= (rank & -rank);
    }
    return result;
}

Дерево двоичного индекса, очевидно, O (nklogn) , но это способностьпоследовательно заполняйте его, что создает возможность использования его для решения.

mapIndex создает ранг для каждого числа в values, так что наименьшее число в values имеет ранг 1.(Например, если values равно «2, 3, 4, 3, 4, 1», тогда mapIndex будет содержать: «{1, 1}, {2, 2}, {3, 3}, {4,5} ". Обратите внимание, что" 4 "имеет ранг" 5 ", потому что есть 2" 3 "в values

binaryIndexTree имеет k различных деревьев, уровень x будет представлять общее количество увеличивающихся подстрок, которые могут быть сформированы длиной x . Любое число в values может создать подстроку длины 1, поэтому каждый элемент будетувеличивайте его ранг и все звания выше него на 1.
На более высоких уровнях увеличивающаяся подстрока зависит от того, имеется ли уже доступная подстрока более короткой длины и более низкого ранга.

Поскольку элементы вставляются в дерево двоичных индексов в соответствии с их порядком в values, порядок вхождения в values сохраняется, поэтому, если элемент был вставлен в binaryIndexTree, то есть потому, что он предшествовал текущему элементув values.

Отличное описание того, как дерево двоичных индексов доступно здесь: http://www.geeksforgeeks.org/binary-indexed-tree-or-fenwick-tree-2/

Вы можете найти исполняемую версию кода здесь: http://ideone.com/GdF0me

0 голосов
/ 28 августа 2016

Давайте рассмотрим пример -

Возьмем массив {7, 4, 6, 8}
Теперь, если вы рассматриваете каждый отдельный элемент также как подпоследовательность, то число возрастающих подпоследовательностеймогут быть сформированы: -
{7} {4} {6} {4,6} {8} {7,8} {4,8} {6,8} {4,6,8}
Для этого массива можно сформировать 9 возрастающую подпоследовательность.
Таким образом, ответ равен 9.

Код выглядит следующим образом -

int arr[] = {7, 4, 6, 8};
int T[] = new int[arr.length];

for(int i=0; i<arr.length; i++)
    T[i] = 1;    

int sum = 1;     
for(int i=1; i<arr.length; i++){
    for(int j=0; j<i; j++){
        if(arr[i] > arr[j]){
            T[i] = T[i] + T[j]; 
        }
    }
    sum += T[i];
}
System.out.println(sum);

Сложность кода O (N log N).

0 голосов
/ 07 ноября 2013

Java-версия в качестве примера:

    int[] A = {1, 2, 0, 0, 0, 4};
    int[] dp = new int[A.length];

    for (int i = 0; i < A.length; i++) {
        dp[i] = 1;

        for (int j = 0; j <= i - 1; j++) {
            if (A[j] < A[i]) {
                dp[i] = dp[i] + dp[j];
            }
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...