Количество отдельных субмассив

Я хочу найти алгоритм для подсчета числа различных подмассивов массива.

Например, в случае A = [1,2,1,2] число различных подмассивов равно 7:

{ [1] , [2] , [1,2] , [2,1] , [1,2,1] , [2,1,2], [1,2,1,2]}  

а в случае B = [1,1,1] число различных подмассивов равно 3:

{ [1] , [1,1] , [1,1,1] }

Подматрица представляет собой непрерывную подпоследовательность или срез массива. Различный означает другое содержание; например:

[1] из A [0: 1] и [1] из A [2: 3] не различны.

и аналогичным образом:

B [0: 1], B [1: 2], B [2: 3] не различны.

Ответ 1

Построить дерево суффиксов для этого массива. Затем добавьте длины всех ребер в это дерево.

Время, необходимое для построения дерева суффикса, - это O (n) с правильным алгоритмом (алгоритмы Ukkonen или McCreight). Время, необходимое для пересечения дерева и суммирования длин, также равно O (n).

Ответ 2

Вы можете тривиально сделать набор подпоследовательностей и подсчитать их, но я не уверен, что это самый эффективный способ, так как это O(n^2).

в python, который будет выглядеть примерно так:

subs = [tuple(A[i:j]) for i in range(0, len(A)) for j in range(i + 1, len(A) + 1)]

uniqSubs = set(subs)

который дает вам:

set([(1, 2), (1, 2, 1), (1,), (1, 2, 1, 2), (2,), (2, 1), (2, 1, 2)])

В двойном цикле понимания понимается сложность O(n²).

Изменить

По-видимому, есть некоторое обсуждение сложности. Создание подмножеств O(n^2), поскольку есть n^2 элементов.

Создание набора из списка O(m), где m - это размер списка, m будет n^2 в этом случае, так как добавление к набору амортизируется O(1).

Таким образом, общее значение O(n^2).

Ответ 3

Изменить: я думаю о том, как уменьшить число итераций/сравнения. Я хочу, чтобы это сделать: если вы получите подматрицу размером n, то все подмассивы размером, меньшим n, уже будут добавлены.

Вот обновленный код.

    List<Integer> A = new ArrayList<Integer>();
    A.add(1);
    A.add(2);
    A.add(1);
    A.add(2);

    System.out.println("global list to study: " + A);

    //global list
    List<List<Integer>> listOfUniqueList = new ArrayList<List<Integer>>();      

    // iterate on 1st position in list, start at 0
    for (int initialPos=0; initialPos<A.size(); initialPos++) {

        // iterate on liste size, start on full list and then decrease size
        for (int currentListSize=A.size()-initialPos; currentListSize>0; currentListSize--) {

            //initialize current list.
            List<Integer> currentList = new ArrayList<Integer>();

            // iterate on each (corresponding) int of global list
            for ( int i = 0; i<currentListSize; i++) {
                currentList.add(A.get(initialPos+i));
            }

            // insure unicity
            if (!listOfUniqueList.contains(currentList)){
                listOfUniqueList.add(currentList);                      
            } else {
                continue;
            }
        }
    }

System.out.println("list retrieved: " + listOfUniqueList);
System.out.println("size of list retrieved: " + listOfUniqueList.size());

глобальный список для изучения: [1, 2, 1, 2]

: [[1, 2, 1, 2], [1, 2, 1], [1, 2], [1], [2, 1, 2], [2, 1], [ 2]]

размер полученного списка: 7

Со списком, содержащим один и тот же patern много раз, число итераций и сравнение будет довольно низким. Для вашего примера [1, 2, 1, 2] строка if (! ListOfUniqueList.contains(currentList)) {выполняется 10 раз. Он только поднимает до 36 для входа [1, 2, 1, 2, 1, 2, 1, 2], который содержит 15 разных подматриц.

Ответ 4

Вправо мой первый ответ был немного светлым.

Я предполагаю, что ответ будет состоять в том, чтобы сгенерировать их все, а затем удалить дубликаты. Или, если вы используете язык, подобный Java, с установленным объектом, создайте все массивы и добавьте их в набор int []. Наборы содержат только один экземпляр каждого элемента и автоматически удаляют дубликаты, поэтому вы можете просто получить размер набора в конце

Ответ 5

Я могу думать о 2 способах...

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

Во-вторых, нужно использовать какое-то вероятное совпадение, а затем развернуться оттуда... если количество элементов одинаково, а общее количество элементов, добавленных вместе, то же самое, а затем проверить verbosely.

Ответ 6

Создайте массив из пары, где каждая пара хранит значение элемента subarray и его индекса.

pair[i] = (A[i],i);

Отсоедините пару в порядке возрастания A[i], а затем уменьшите порядок i.

Рассмотрим пример A = [1,3,6,3,6,3,1,3];
пар после сортировки будет pair = [(1,6),(1,0),(3,7),(3,5),(3,3),(3,1),(6,4),(6,2)]

pair[0] имеет элемент index 6. Из index 6 мы можем иметь два суб-массива [1] и [1,3]. Итак, ANS = 2,
Теперь возьмите каждую последовательную пару один за другим.
Принимая pair[0] и pair[1],
pair[1] имеет индекс 0. Мы можем иметь 8 подмассивов, начиная с index 0. Но уже учтены два подмассива [1] и [1,3]. Поэтому, чтобы удалить их, нам нужно сравнить самый длинный общий префикс sub-array для pair[0] и pair[1]. Самая длинная общая длина префикса для индексов, начинающихся с 0 и 6, равна 2 i.e [1,3].
Таким образом, теперь новые четкие подмассивы будут [1,3,6].. to [1,3,6,3,6,3,1,3] т.е. 6 подмассивов. Таким образом, новое значение ANS равно 2 + 6 = 8;

Итак, для pair[i] и pair[i+1]
ANS = ANS + Number of sub-arrays beginning from pair[i+1] - Length of longest common prefix.

Элемент сортировки принимает O (n logn).
Итерация каждой последовательной пары - это O (n), и для каждой итерации наибольший общий префикс принимает O (n), делая всю итерационную часть O (n ^ 2). Это лучшее, что я мог получить.

Вы можете видеть, что для этого нам не нужна пара. Первое значение пары, значение элемента не было обязательным. Я использовал это для лучшего понимания. Вы всегда можете пропустить это.