Re: Задачки с Amazon SDE Interview
От: StatujaLeha на правах ИМХО
Дата: 16.12.20 14:20
Оценка:
Здравствуйте, Chriso, Вы писали:

C>2. Дан список последовательностей чисел

C>Найти самую часто встречающуюся непрерывную подпоследовательность и сколько раз она встречается.
C>Облегчённый вариант, с которым я также не справился: длина подпоследовательности задана, рассматривать только подпоследовательности этой длины.
C>Очевидное решение с Dictionary, где ключом является сама подпоследовательность не подходит по требуемой памяти, даже в "облегчённом" варианте. Предполагается, что список читается из файла и в память не влезет.

Весь список списков(т.е. целый файл в память не влезает), а один список из входного списка влезает в память?
Re[8]: Задачки с Amazon SDE Interview
От: Буравчик Россия  
Дата: 16.12.20 14:28
Оценка:
Здравствуйте, MaximVK, Вы писали:

MVK>Да, судя по всему так оно и есть. Но если мы решаем задачу для K, то потом можно решить для K+1,

MVK>сузив поиск для подпоследовательностей содержащих подпоследовательности найденные для K.

Как мы запомним подпоследовательности из предыдущего К? Ведь память должна быть O(1)

MVK>Я исхожу из того, что длинна строки (== последовательности) ограничена и влазит в нашу константную память.

MVK>В этом случае предложенное решение будет N^2, где N количество строк, так как обработка одной строки в этом случае становится константой.

Что-то я запутался.
Пусть P — длина строки, N — количество строк. Превратить длину строки P в константу мы всегда сможем, а сейчас предлагаю ее учитывать.
Как работает алгоритм и какова его сложность?
Best regards, Буравчик
Re[9]: Задачки с Amazon SDE Interview
От: MaximVK Россия  
Дата: 16.12.20 15:19
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Как мы запомним подпоследовательности из предыдущего К? Ведь память должна быть O(1)

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

MVK>>Я исхожу из того, что длинна строки (== последовательности) ограничена и влазит в нашу константную память.

MVK>>В этом случае предложенное решение будет N^2, где N количество строк, так как обработка одной строки в этом случае становится константой.

Б>Что-то я запутался.

Б>Пусть P — длина строки, N — количество строк. Превратить длину строки P в константу мы всегда сможем, а сейчас предлагаю ее учитывать.
Б>Как работает алгоритм и какова его сложность?

Сходу только в брутфорсном варианте когда мы для для каждой подстроки считаем количество вхождений сканируя весь файл (считаем, что поиск подстроки стоит нам O(P)):
для i in 2..P (i = длина подпоследовательности)
N * (N-1) * (P-i) * P
получаем
O(N^2*P^3)

В умном варианте надо подумать, сходу не скажу. Постараюсь вечером прикинуть.
Отредактировано 16.12.2020 16:28 MaximVK . Предыдущая версия . Еще …
Отредактировано 16.12.2020 15:54 MaximVK . Предыдущая версия .
Отредактировано 16.12.2020 15:20 MaximVK . Предыдущая версия .
Re: Задачки с Amazon SDE Interview
От: scf  
Дата: 16.12.20 15:27
Оценка:
Здравствуйте, Chriso, Вы писали:


C>2. Дан список последовательностей чисел

C>Найти самую часто встречающуюся непрерывную подпоследовательность и сколько раз она встречается.
C>Облегчённый вариант, с которым я также не справился: длина подпоследовательности задана, рассматривать только подпоследовательности этой длины.
C>Очевидное решение с Dictionary, где ключом является сама подпоследовательность не подходит по требуемой памяти, даже в "облегчённом" варианте. Предполагается, что список читается из файла и в память не влезет.

Что-то здесь не так. Даже упрощенная задача "найти самое часто встречающееся число в потоке" требует хранения всех чисел со счетчиками в памяти
Re[2]: Задачки с Amazon SDE Interview
От: MaximVK Россия  
Дата: 16.12.20 15:32
Оценка:
Здравствуйте, scf, Вы писали:

scf>Что-то здесь не так.

определенно, не хватает ограничений. или не сказали или предполагалось, что топикстартер должен их был спросить.

scf>Даже упрощенная задача "найти самое часто встречающееся число в потоке" требует хранения всех чисел со счетчиками в памяти

ну файл можно несколько раз зачитать
Re[3]: Задачки с Amazon SDE Interview
От: scf  
Дата: 16.12.20 16:11
Оценка:
Здравствуйте, MaximVK, Вы писали:

scf>>Даже упрощенная задача "найти самое часто встречающееся число в потоке" требует хранения всех чисел со счетчиками в памяти

MVK>ну файл можно несколько раз зачитать

Мне что-то не приходит в голову, как чтение файла несколько раз может снизить потребность в памяти.
Re[4]: Задачки с Amazon SDE Interview
От: MaximVK Россия  
Дата: 16.12.20 16:17
Оценка: 4 (1)
Здравствуйте, scf, Вы писали:

scf>Мне что-то не приходит в голову, как чтение файла несколько раз может снизить потребность в памяти.


Ну в твоем примере, для каждого числа перечитываешь файл и считаешь количество вхождений.
В этом случае нужно помнить только число и кол.-во вхождений.
Re: Задачки с Amazon SDE Interview
От: Sergei MO Россия  
Дата: 16.12.20 17:50
Оценка:
Здравствуйте, Chriso, Вы писали:

C>2. Дан список последовательностей чисел

C>Найти самую часто встречающуюся непрерывную подпоследовательность и сколько раз она встречается.
C>Облегчённый вариант, с которым я также не справился: длина подпоследовательности задана, рассматривать только подпоследовательности этой длины.
C>Очевидное решение с Dictionary, где ключом является сама подпоследовательность не подходит по требуемой памяти, даже в "облегчённом" варианте. Предполагается, что список читается из файла и в память не влезет.

Во-первых, нужно дополнительное условие вроде "среди всех самых часто встречающихся последовательностей найти самую длинную". Иначе допустимым ответом будет последовательность длины 1 из самого часто встречающегося числа.
Во-вторых, было бы неплохо указать остальные ограничения по памяти — влезает ли в память один список, влезает ли в память Dictionary со всеми числами из всех последовательностей, влезает ли в память ответ, и т.д.

Я пока приведу решение для одной последовательности и случая, когда в память влезает всё.

Пусть у нас есть ответ — подпоследовательность, встречающаяся C раз. Тогда для неё выполняются следующие утверждения:
1) Любое число, входящее в эту подпоследовательность, встречается в ней ровно один раз. (Если оно встречается несколько раз, то последовательность длины 1, состоящая из этого числа, встретится k*C раз, т.е. чаще, чем ответ).
2) Любое число, входящее в эту подпоследовательность, встречается в исходной последовательности только во вхождениях ответа. (Если оно встречается где-то ещё, то последовательность длины 1 из этого числа, встретится C+k раз — чаще чем ответ).

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

Решение следующее. На первом проходе считаем вхождения всех чисел, и составляем набор чисел, имеющих максимальное количество вхождений. Ответ будет состоять только из них.
На втором проходе жадным алгоритмом строим последовательности, удовлетворяющие приведенным выше условиям, и выбираем самую длинную из них.
Сложность решения — O(N*Log(N)), где N — длина входной последовательности. Хотя нет, нашёл ошибку и теперь там O(N*N) вылезает.

  Код решения
#include <iostream>
#include <vector>
#include <deque>
#include <map>

using namespace std;


class SequenceSolver
{
public:
    SequenceSolver(vector<int>& sequence);

    int getCount() { return maxCount_; }

    vector<int> getSequence();

private:
    // находит все числа-кандидаты для построения последовательностей
    void buildNumbers(vector<int>& sequence);

    // прекращает построение/проверку активной последовательности
    void terminateCurrentSequence();

    // делит последовательность на две по элементу с индексом position
    size_t splitSequence(deque<int>& sequence, size_t position);

    // начинает построение новой последовательности
    void beginSequence(size_t& index, int number);

    // начинает проверку существующей последовательности
    void beginSequenceCheck(size_t index, size_t position);

private:
    static const size_t NONE = (size_t)(-1);

    // числа-кандидаты для построения последовательностей
    // значение - (индекс последовательности, позиция в последовательности)
    map<int, pair<size_t, size_t>> numbers_;

    // последовательности-кандидаты
    vector<deque<int>> sequences_;

    // индекс и текущая позиция активной последовательности
    // (которая строится или проверяется в данный момент)
    size_t activeIndex_;
    size_t activePosition_;

    // true, если активная последовательность встретилась впервые
    bool activeNew_;

    // сколько раз встречается искомая последовательность
    int maxCount_;
};


SequenceSolver::SequenceSolver(vector<int>& sequence) : maxCount_(0), activeIndex_(NONE)
{
    buildNumbers(sequence);

    for (size_t i = 0; i < sequence.size(); i++)
    {
        auto it = numbers_.find(sequence[i]);
        if (it == numbers_.end())
        {
            terminateCurrentSequence();
        }
        else
        {
            if (activeIndex_ == NONE) // нет активной последовательности
            {
                if (it->second.first == NONE)
                {
                    beginSequence(it->second.first, it->first);
                }
                else
                {
                    beginSequenceCheck(it->second.first, it->second.second);
                }
            }
            else if (activeNew_) // есть новая активная последовательность
            {
                if (it->second.first == NONE)
                {
                    // продолжаем новую активную последовательность
                    it->second.first = activeIndex_;
                    it->second.second = activePosition_;
                    sequences_[activeIndex_].push_back(it->first);
                    activePosition_ += 1;
                }
                else
                {
                    // прерываем новую активную последовательность
                    // и начинаем проверку старой активной последовательности
                    terminateCurrentSequence();
                    beginSequenceCheck(it->second.first, it->second.second);
                }
            }
            else // есть старая активная последовательность
            {
                if (it->second.first == NONE)
                {
                    // прерываем старую активную последовательность
                    // и начинаем новую активную последовательность
                    terminateCurrentSequence();
                    beginSequence(it->second.first, it->first);
                }
                else if (it->second.first == activeIndex_ && it->second.second == activePosition_)
                {
                    // продолжаем проверку старой активной последовательности
                    activePosition_ += 1;
                    if (activePosition_ == sequences_[activeIndex_].size())
                    {
                        activeIndex_ = NONE;
                    }
                }
                else
                {
                    // прерываем старую активную последовательность
                    // и начинаем проверку другой старой активной последовательности
                    terminateCurrentSequence();

                    // обработка случая, когда число находится в этой же последовательности но на более
                    // далёкой позиции, например:  [ 1 2 3 4 5 ] и [ 1 2 5 ]
                    if (numbers_.find(sequence[i]) == numbers_.end())
                    {
                        activeIndex_ = NONE;
                    }
                    else
                    {
                        beginSequenceCheck(it->second.first, it->second.second);
                    }
                }
            }
        }
    }
}

vector<int> SequenceSolver::getSequence()
{
    size_t max = 0;
    for (size_t i = 1; i < sequences_.size(); i++)
    {
        if (sequences_[i].size() > sequences_[max].size())
        {
            max = i;
        }
    }

    return vector<int>(sequences_[max].begin(), sequences_[max].end());
}

void SequenceSolver::buildNumbers(vector<int>& sequence)
{
    map<int, int> counts;
    for (size_t i = 0; i < sequence.size(); i++)
    {
        counts[sequence[i]]++;
    }

    maxCount_ = 0;
    for (auto it = counts.begin(); it != counts.end(); ++it)
    {
        maxCount_ = it->second > maxCount_ ? it->second : maxCount_;
    }

    for (auto it = counts.begin(); it != counts.end(); ++it)
    {
        if (it->second == maxCount_)
        {
            numbers_[it->first] = make_pair(NONE, 0);
        }
    }
}

void SequenceSolver::terminateCurrentSequence()
{
    if (activeIndex_ != NONE && !activeNew_)
    {
        splitSequence(sequences_[activeIndex_], activePosition_);
    }
    activeIndex_ = NONE;
}

size_t SequenceSolver::splitSequence(deque<int>& sequence, size_t position)
{
    deque<int> temp(sequence.begin() + position, sequence.end());
    sequence.erase(sequence.begin() + position, sequence.end());
    size_t index = NONE;
    if (temp.size() > 0)
    {
        index = sequences_.size();
        for (size_t i = 0; i < temp.size(); i++)
        {
            numbers_[temp[i]] = make_pair(index, i);
        }
        sequences_.push_back(temp);
    }
    return index;
}

void SequenceSolver::beginSequence(size_t& index, int number)
{
    index = sequences_.size();
    sequences_.push_back(deque<int>());
    sequences_.back().push_back(number);
    activeIndex_ = index;
    activePosition_ = 1;
    activeNew_ = true;
}

void SequenceSolver::beginSequenceCheck(size_t index, size_t position)
{
    activeIndex_ = splitSequence(sequences_[index], position);
    activePosition_ = 1;
    activeNew_ = false;
    if (activePosition_ == sequences_[activeIndex_].size())
    {
        activeIndex_ = NONE;
    }
}

int main()
{
    int arr[] = { 1, 2, 3, 4, 0, 4, 2, 3, 2, 3 };
    
    SequenceSolver sol(vector<int>(arr, arr + sizeof(arr) / sizeof(int)));
    vector<int> ans = sol.getSequence();

    cout << "N = " << sol.getCount() << endl;
    cout << ans[0];
    for (size_t i = 1; i < ans.size(); i++)
    {
        cout << " " << ans[i];
    }
    cout << endl;
    return 0;
}
Отредактировано 16.12.2020 19:05 Sergei MO . Предыдущая версия .
Re[2]: Задачки с Amazon SDE Interview
От: Sergei MO Россия  
Дата: 16.12.20 22:04
Оценка:
Придумал, как сделать ещё проще.

Если в ответе за числом X идёт число Y, то в исходной последовательности будет то же самое — за числом X всегда будет идти число Y.

Поэтому для решения задачи нам нужно посчитать количество вхождений каждого числа, и выяснить, идёт ли за ним всегда одно и то же число, или могут идти разные (конец последовательности трактуется как "идут разные числа").

Далее рассматриваем только числа, имеющие максимальное количество вхождений. По сути у нас есть ориентированный граф, вершины которого — числа, а дуги строятся по правилу: если за числом X всегда идёт число Y, то есть дуга X->Y. Ответом исходной задачи является самый длинный путь в этом графе. Поскольку исходящих дуг у каждой вершины не более одной, и отсутствуют циклы, то поиск такого пути тривиален.

И сложность решения теперь O(N*Log(N)).

  Код решения
#include <iostream>
#include <vector>
#include <stack>
#include <map>

using namespace std;

void solve(vector<int>& sequence)
{
    // для каждого числа считаем, сколько раз оно встречается
    // и одинаковые ли числа идут после него
    // если одинаковые - запоминаем их во втором поле, если разные - записываем -1
    map<int, pair<int, int>> numbers;
    for (size_t i = 0; i < sequence.size(); i++)
    {
        int nextNumber = i + 1 < sequence.size() ? sequence[i + 1] : -1;
        auto it = numbers.find(sequence[i]);
        if (it == numbers.end())
        {
            it = numbers.insert(make_pair(sequence[i], make_pair(1, nextNumber))).first;
        }
        else
        {
            it->second.first++;
            if (it->second.second != nextNumber)
            {
                it->second.second = -1;
            }
        }
    }

    int maxCount = 0;
    for (auto it = numbers.begin(); it != numbers.end(); ++it)
    {
        maxCount = it->second.first > maxCount ? it->second.first : maxCount;
    }

    // удаляем все числа, встречающиеся менее максимального числа раз
    // одновременно инициализируем поле счетчика для запоминания длины последовательности
    auto it = numbers.begin();
    while (it != numbers.end())
    {
        auto tmp = it++;
        if (tmp->second.first != maxCount)
        {
            numbers.erase(tmp);
        }
        else
        {
            tmp->second.first = -1;
        }
    }

    // поиск в глубину из каждого числа с кэшированием результата
    stack<int> stk;
    for (auto it = numbers.begin(); it != numbers.end(); ++it)
    {
        if (it->second.first == -1)
        {
            auto tmp = it;
            while (tmp->second.first == -1 && tmp->second.second != -1)
            {
                stk.push(tmp->first);
                tmp = numbers.find(tmp->second.second);
            }
            if (tmp->second.second == -1)
            {
                tmp->second.first = 0;
            }
            while (!stk.empty())
            {
                tmp = numbers.find(stk.top());
                tmp->second.first = numbers.find(tmp->second.second)->second.first + 1;
                stk.pop();
            }
        }
    }

    // ищем число, из которого начинается путь максимальной длины
    int maxLength = 0;
    int maxNumber = 0;
    for (auto it = numbers.begin(); it != numbers.end(); ++it)
    {
        if (it->second.first > maxLength)
        {
            maxLength = it->second.first;
            maxNumber = it->first;
        }
    }

    cout << "N = " << maxCount << endl;
    cout << maxNumber;
    it = numbers.find(maxNumber);
    while (it->second.second != -1)
    {
        it = numbers.find(it->second.second);
        cout << " " << it->first;
    }
    cout << endl;
}


int main()
{
    int arr[] = { 1, 2, 3, 4, 0, 4, 2, 3, 2, 3 };
    solve(vector<int>(arr, arr + sizeof(arr) / sizeof(int)));
    return 0;
}
Re[3]: Задачки с Amazon SDE Interview
От: Буравчик Россия  
Дата: 17.12.20 07:25
Оценка:
Здравствуйте, Sergei MO, Вы писали:

SM>Придумал, как сделать ещё проще.

SM>И сложность решения теперь O(N*Log(N)).
SM>int arr[] = { 1, 2, 3, 4, 0, 4, 2, 3, 2, 3 };

Решение ломается, если в конец последовательности добавить 2, 4.
Best regards, Буравчик
Re[4]: Задачки с Amazon SDE Interview
От: Sergei MO Россия  
Дата: 17.12.20 07:56
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Решение ломается, если в конец последовательности добавить 2, 4.


Да, есть ошибка.
Переменная maxLength должна быть инициализирована не 0, а -1.
Re[5]: Задачки с Amazon SDE Interview
От: Буравчик Россия  
Дата: 17.12.20 10:00
Оценка:
Здравствуйте, Sergei MO, Вы писали:

SM>Переменная maxLength должна быть инициализирована не 0, а -1.


Ну нет же. Ошибка в подходе.

Исправил 0 на -1. На массиве { 1, 2, 3, 4, 0, 4, 2, 3, 2, 3, 2, 4 } выдает ответ

N = 4
2


Т.е. в качестве ответа предложена последовательность {2}, а правильный ответ {2, 3}
Best regards, Буравчик
Re[6]: Задачки с Amazon SDE Interview
От: watchmaker  
Дата: 17.12.20 10:36
Оценка: +2
Здравствуйте, Буравчик, Вы писали:

Б>На массиве { 1, 2, 3, 4, 0, 4, 2, 3, 2, 3, 2, 4 } выдает ответ

Б>

Б>N = 4
Б>2


Б>Т.е. в качестве ответа предложена последовательность {2}, а правильный ответ {2, 3}



С чего-то ответ [2, 3] является правильным? Невооруженным взглядом видно, что в этом массиве эта непрерывная подпоследовательность встречается три раза. Хотя другая подпоследовательность ([2]), которая встречается четыре раза. Так что это сразу свидетельствует том, что [2, 3] не может быть самой частвовстречаемой.
Re[7]: Задачки с Amazon SDE Interview
От: Буравчик Россия  
Дата: 17.12.20 10:48
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>С чего-то ответ [2, 3] является правильным? Невооруженным взглядом видно, что в этом массиве эта непрерывная подпоследовательность встречается три раза. Хотя другая подпоследовательность ([2]), которая встречается четыре раза. Так что это сразу свидетельствует том, что [2, 3] не может быть самой частвовстречаемой.


Ну, ок. Я пытался привести пример, когда за двойкой будет следовать другой элемент (четверка вместо тройки), и алгоритм ошибется.

Вот для такого массива { 1, 2, 3, 4, 0, 4, 2, 3, 2, 3, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4 }

N = 9
2


Правильный ответ {2, 4} и N=6

ДОБАВЛЕНО:
Если уж что-то и реализовывать из достаточно простых и эффективных алгоритмов, то это алгоритм на суффиксных массивах (не дереве).
— формируем массив суффиксов (можно просто хранить начальный индекс суффикса)
— сортируем все суффиксы по алфавиту (лексикографически)
— пробегаем по списку и находим общие префиксы у суффиксов (сравнивая соседние элементы)
Выводим ответ — максимальный найденный префикс
Best regards, Буравчик
Отредактировано 17.12.2020 10:53 Буравчик . Предыдущая версия . Еще …
Отредактировано 17.12.2020 10:49 Буравчик . Предыдущая версия .
Re[8]: Задачки с Amazon SDE Interview
От: watchmaker  
Дата: 17.12.20 10:54
Оценка: +2
Здравствуйте, Буравчик, Вы писали:


Б>Вот для такого массива { 1, 2, 3, 4, 0, 4, 2, 3, 2, 3, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4 }

Б>

Б>N = 9
Б>2


Б>Правильный ответ {2, 4} и N=6


Но почему он правильный?
Ведь эта подпоследовательность встречается только 6 раз. Хотя в этот массив входит и другая подпоследовательность (всё та же [2]), которая встречается целых 9 раз. Что явно чаще
Re[9]: Задачки с Amazon SDE Interview
От: Буравчик Россия  
Дата: 17.12.20 10:57
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Ведь эта подпоследовательность встречается только 6 раз. Хотя в этот массив входит и другая подпоследовательность (всё та же [2]), которая встречается целых 9 раз. Что явно чаще


Точно
Best regards, Буравчик
Re[9]: Задачки с Amazon SDE Interview
От: Буравчик Россия  
Дата: 17.12.20 11:01
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Но почему он правильный?

W>Ведь эта подпоследовательность встречается только 6 раз. Хотя в этот массив входит и другая подпоследовательность (всё та же [2]), которая встречается целых 9 раз. Что явно чаще

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

А условие "рассматривать только подпоследовательности этой длины" (в облегченной задаче!) намекает нам, что наша задача не про подсчет элементов, а про поиск последовательности (т.е. хотя бы из двух элементов).
Best regards, Буравчик
Отредактировано 17.12.2020 11:02 Буравчик . Предыдущая версия .
Re[10]: Задачки с Amazon SDE Interview
От: watchmaker  
Дата: 17.12.20 17:33
Оценка: +1
Здравствуйте, Буравчик, Вы писали:

Б>А условие "рассматривать только подпоследовательности этой длины" (в облегченной задаче!) намекает нам, что наша задача не про подсчет элементов, а про поиск последовательности (т.е. хотя бы из двух элементов).


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

То есть из исходного сообщения понятно из какой области спрашивали знания. В целом интересно. Как раз при подготовке к собеседованию ясно какие разделы нужно повторить. Но условия задач лучше искать в другом месте.
Re[2]: Задачки с Amazon SDE Interview
От: sergeya Ниоткуда http://blogtani.ru
Дата: 17.12.20 17:55
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>
Б>            if word == subword:
Б>                result.append([subword])
Б>                continue
Б>


Не отсечет ли это условие другие допустимые варианты комбинаций?

Пусть word можно составить как из subword1, так из subword2 + subword3.
Приведенное условие имхо остановит перебор как только встретится subword1.
Мобильная версия сайта RSDN — http://rsdn.org/forum/rsdn/6938747
Автор: sergeya
Дата: 19.10.17
Re: Задачки с Amazon SDE Interview
От: sergeya Ниоткуда http://blogtani.ru
Дата: 17.12.20 18:02
Оценка:
Здравствуйте, Chriso, Вы писали:

C>1. Дан список строк S[1..N], нужно вернуть список R всех возможных списков таких, что:

C>R[i][j] ∈ S
C>string.Join("", R[i]) ∈ S
C>R[i].Count >= 2

C>Пример:

C>вход [a, b, ab, cd, abcd, fgh, f, g, h]
C>выход [[a, b], [a, b, cd], [ab, cd], [f, g, h]]

Перебираем все слова в input.
Отсекаем те, длина которы меньше 2-х символов, т.к. для них не будет решений, удоволетворяющих условию R[i].Count >= 2
Для каждого слова перебираем все возможные комбинации head и tail (скользящий splitIndex)
Если head найден в input, то рекурсивно ищем все возможные комбинации для tail.
Из найденных комбинаций убираем состоящие из одного элемента.


class Program
{
    private static string[] input = new[] { "a", "b", "ab", "cd", "abcd", "fgh", "f", "g", "h" };

    static void Main()
    {
        var output = input
            .Where(term => term.Length >= 2)              // Заранее отсекаем все слова, которые короче 2-х символов
            .SelectMany(term => FindCombinations(term))   // Ищем все возможные комбинации для каждого слова 
            .Where(list => list.Count() >= 2);            // Отсекаем комбинации, которые состоят из менее 2-х элементов

        Console.WriteLine(JsonConvert.SerializeObject(output));

        Console.ReadLine();
    }

    static IEnumerable<IEnumerable<string>> FindCombinations(string term)
    {
        var splitIndex = 1;
        while (splitIndex <= term.Length)
        {
            var (head, tail) = (term.Substring(0, splitIndex), term.Substring(splitIndex++));
            if (input.Contains(head))
            {
                if (tail.Length == 0)
                    yield return new[] { head };
                else
                    foreach (var result in FindCombinations(tail))
                        yield return new[] { head }.Concat(result);
            }
        }
    }
}
Мобильная версия сайта RSDN — http://rsdn.org/forum/rsdn/6938747
Автор: sergeya
Дата: 19.10.17
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.