Найдите все самые большие подсписки из списка, в котором хотя бы два элемента равны

Учитывая список объектов и нетранзитивную функцию равенства, которая возвращает true, когда два объекта равны, а в противном случае возвращает false, мне нужно найти все самые большие подсписки, в которых хотя бы два объекта равны. Например -

val list = List(o1, o2, o3, o4, o5)

а также,

isEqual(o1, o2) => true
isEqual(o2, o4) => true
isEqual(o3, o5) => true

Результат будет:

List(o1, o2, o4)
List(o3, o5)

Обратите внимание, что isEqual не является транзитивным, т.е. в приведенном выше случае o1 может не совпадать с o4, даже если они принадлежат одному и тому же подсписку.


person Shirish Kumar    schedule 08.08.2017    source источник
comment
Если хотя бы два объекта должны быть равны, то не является ли сам список самой большой подпоследовательностью, поскольку он уже содержит хотя бы одну пару равных объектов?   -  person EvilTak    schedule 08.08.2017
comment
@Evil Tak - ты прав. Я просто хотел быть откровенным.   -  person Shirish Kumar    schedule 08.08.2017
comment
Одно очевидное решение состоит в том, чтобы сначала сгенерировать все возможные равные кортежи, что займет O (N ^ 2), а затем найти связанные компоненты. Мне интересно, можно ли это сделать быстрее.   -  person Shirish Kumar    schedule 08.08.2017


Ответы (2)


Ваша задача аналогична задаче поиска всех компонент связности графа.

Итак, первое, что нужно сделать, это преобразовать ваш список в граф G(V, E), где V обозначает вершины, а E обозначает ребра:

V = list
E = {(o1,o2) for all o1,o2 in list| o1.Equals(o2)}

После этого сделайте DFS, чтобы найти все компоненты

WHILE-EXISTS unvisted node in G DO
     component[i] = DFS(G)
END

Конечно, сами компоненты Graphs. Компоненты — это списки, которые вы ищете, а вершины в компонентах — это элементы списка.

Для вашего примера график будет выглядеть так

введите здесь описание изображения

ВНИМАНИЕ: Поскольку вам нужно сравнивать каждый объект, разговор займет O (n ^ 2). Чтобы найти все компоненты, вам потребуется O (n). Таким образом, этот алгоритм имеет асимптотическое время выполнения O (n ^ 2)

Ответ на комментарий в вашем вопросе

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

person Andreas    schedule 08.08.2017
comment
Я только что добавил комментарий в вопросе - person Shirish Kumar; 08.08.2017

Вы можете использовать алгоритм объединения непересекающихся множеств, чтобы найти все связанные компоненты. А затем распечатать список. Временная сложность приведенного ниже кода составляет O (NlogN). Weighted_union уменьшает временную сложность объединения до logN. Поэтому, если я выполню объединение N раз, в худшем случае это займет NlogN.

#include <bits/stdc++.h>
using namespace std;

int Arr[100], size[100];

int root (int i)
{
    while(Arr[ i ] != i)
    {
        Arr[ i ] = Arr[ Arr[ i ] ] ; 
        i = Arr[ i ]; 
    }
    return i;
}

void weighted_union(int A,int B)
{
    int root_A = root(A);
    int root_B = root(B);
    if(size[root_A] < size[root_B ])
    {
        Arr[ root_A ] = Arr[root_B];
        size[root_B] += size[root_A];
    }
    else
    {
        Arr[ root_B ] = Arr[root_A];
        size[root_A] += size[root_B];
    }
}

void initialize( int N)
{
    for(int i = 0;i<N;i++)
    {
        Arr[ i ] = i ;
        size[ i ] = 1;
    }
}

int main() {
    // your code goes here
    initialize(6);
    weighted_union(1,2);
    weighted_union(2,4);
    weighted_union(3,5);


    map<int, vector<int> >m;
    for (int i=1;i<=5;i++) {
        if(m.find(Arr[i])!=m.end()){
            vector<int> x = m[Arr[i]];
            x.push_back(i);
            m[Arr[i]] = x;
        } else {
            vector<int> x;
            x.push_back(i);
            m[Arr[i]]=x;
        }
    }

    for (std::map<int,vector<int> >::iterator it=m.begin(); it!=m.end(); ++it) {
        vector<int> x = it->second;
        for(int j=0;j<x.size();++j) {
            cout<<x[j]<<" ";
        }
        cout<<endl;
    }

    return 0;
}

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

person sourabh1024    schedule 08.08.2017
comment
Спасибо за ответ. Но при таком подходе все равно потребуется O(N^2), чтобы найти все возможные объединения. - person Shirish Kumar; 08.08.2017
comment
Нет, это даст вам все возможные союзы в O (NlogN) + O ( Different_set * No_of_elements_in_set) - person sourabh1024; 08.08.2017
comment
Вы можете узнать больше о его временной сложности здесь: hackerearth.com /practice/notes/disjoint-set-union-union-find - person sourabh1024; 08.08.2017
comment
То, что вы описали выше, - это проблема поиска взвешенного союза. Выше вы предполагали, что доступны три объединения (weighted_union(1,2), weighted_union(2,4) и weighted_union(3,5)). Если я что-то не упустил, в моем случае, если я использую поиск объединения или подход связанных компонентов, мне нужно сначала найти эти соединения кортежей - и это займет n ^ 2 времени. - person Shirish Kumar; 08.08.2017
comment
Да, это потребует всех отношений. вы не можете оптимизировать это, поскольку оно не является транзитивным по своей природе - person sourabh1024; 08.08.2017