Найти наименьшее окно входного массива, содержащее все элементы массива запросов - PullRequest
8 голосов
/ 19 сентября 2010

Проблема: Для заданного входного массива целых чисел размера n и массива запроса целых чисел размера k найдите наименьшее окно входного массива, которое содержит все элементы массива запросов, а также в том же порядке.

Я попробовал следующий подход.

        int[] inputArray = new int[] { 2, 5, 2, 8, 0, 1, 4, 7 };
        int[] queryArray = new int[] { 2, 1, 7 };

Найдет положение всех элементов массива запросов в inputArray.

public static void SmallestWindow(int[] inputArray, int[] queryArray)
    {
        Dictionary<int, HashSet<int>> dict = new Dictionary<int, HashSet<int>>();

        int index = 0;
        foreach (int i in queryArray)
        {
            HashSet<int> hash = new HashSet<int>();
            foreach (int j in inputArray)
            {
                index++;
                if (i == j)
                    hash.Add(index); 
            }
            dict.Add(i, hash);
            index = 0;
        }
      // Need to perform action in above dictionary.??
    }

Я получил следующий словарь

  1. int 2 -> position {1, 3}
  2. int 1 -> position {6}
  3. int 7 -> position {8}

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

  1. Сравните int 2 position с int 1 position.Как (6-3) <(6-1). Таким образом, я сохраню 3, 6 в хэш-карте. </p>

  2. Сравним позиции int 1 и int 7, как указано выше..

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

Ответы [ 8 ]

4 голосов
/ 19 сентября 2010

Алгоритм:
Для каждого элемента в массиве запросов сохраните в карте M (V → (I, P)), V - элемент, I - индекс во входном массиве, P - положение в массиве запросов. (Индекс во входном массиве для некоторого P является наибольшим, так что запрос [0..P] является подпоследовательностью ввода [I..curr])

Итерация по массиву.
Если значение является первым термином в массиве запросов: сохраните текущий индекс как I.
Иначе: сохраните значение индекса предыдущего элемента в массиве запросов, например, M[currVal].I = M[query[M[currVal].P-1]].I.
Если значение является последним термином: проверьте, является ли [I..curr] новым лучшим.

Сложность
Сложность этого O (N) , где N - размер входного массива.

нотабене
Этот код ожидает, что в массиве запросов не повторяются никакие элементы. Для этого мы можем использовать карту M (V → listOf ((I, P))). Это O (N hC (Q)), где hC (Q) - счетчик режима для массива запросов.
Еще лучше было бы использовать M (V → listOf ((connectedList (I), P))). Там, где повторяющиеся элементы появляются последовательно в массиве запросов, мы используем связанный список. Обновление этих значений становится O (1). Тогда сложность составляет O (N
hC (D (Q))), где D (Q) - это Q с объединенными последовательными членами.

Осуществление
Пример реализации java доступен здесь . Это не работает для повторяющихся элементов в массиве запросов, а также для проверки ошибок и т. Д.

0 голосов
/ 22 июня 2011

На всякий случай, если кто-то заинтересован в реализации C ++ с O (nlog (k))

    void findMinWindow(const vector<int>& input, const vector<int>& query) {
         map<int, int> qtree;
         for(vector<int>::const_iterator itr=query.begin(); itr!=query.end(); itr++) {
            qtree[*itr] = 0;
         }

         int first_ptr=0;
         int begin_ptr=0;

         int index1 = 0;
         int queptr = 0;

         int flip = 0;

         while(true) {
             //check if value is in query
             if(qtree.find(input[index1]) != qtree.end()) {
                int x = qtree[input[index1]];
                if(0 == x) {
                  flip++;
                }
                qtree[input[index1]] = ++x;
              }

              //remove all nodes that are not required and
              //yet satisfy the all query condition.
              while(query.size() == flip) {
                //done nothing more
                if(queptr == input.size()) {
                  break;
                }

                //check if queptr is pointing to node in the query
                if(qtree.find(input[queptr]) != qtree.end()) {
                  int y = qtree[input[queptr]];
                  //more nodes and the queue is pointing to deleteable node
                  //condense the nodes
                  if(y > 1) {
                    qtree[input[queptr]] = --y;
                    queptr++;
                  } else {
                    //cant condense more just keep that memory
                    if((!first_ptr && !begin_ptr) ||
                        ((first_ptr-begin_ptr)>(index1-queptr))) {
                      first_ptr=index1;
                      begin_ptr=queptr;
                    }
                    break;
                  }
                } else {
                  queptr++;
                }
              }

             index1++;

             if(index1==input.size()) {
                break;
             }
         }
         cout<<"["<<begin_ptr<<" - "<<first_ptr<<"]"<<endl;
    }

здесь главное для его вызова.

    #include <iostream>
    #include <vector>
    #include <map>

    using namespace std;

    int main() {
        vector<int> input;
        input.push_back(2);
        input.push_back(5);
        input.push_back(2);
        input.push_back(8);
        input.push_back(0);
        input.push_back(1);
        input.push_back(4);
        input.push_back(7);

        vector<int> query1;
        query1.push_back(2);
        query1.push_back(8);
        query1.push_back(0);

        vector<int> query2;
        query2.push_back(2);
        query2.push_back(1);
        query2.push_back(7);

        vector<int> query3;
        query3.push_back(1);
        query3.push_back(4);

        findMinWindow(input, query1);
        findMinWindow(input, query2);
        findMinWindow(input, query3);
    }
0 голосов
/ 28 ноября 2010

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

0 голосов
/ 20 сентября 2010

Спасибо всем за ваш вклад. Я немного изменил свой код и считаю, что он работает. Хотя это может быть не очень эффективно, но я рад решить, используя мою голову :). Пожалуйста, оставьте свой отзыв

Вот мой класс Pair с числом и позицией в качестве переменной

    public class Pair
    {
    public int Number;
    public List<int> Position;
    }

Вот метод, который возвращает список всех пар.

     public static Pair[]  GetIndex(int[] inputArray, int[] query)
      {
        Pair[] pairList = new Pair[query.Length]; 
        int pairIndex = 0;
        foreach (int i in query)
        {
            Pair pair = new Pair();
            int index = 0;
            pair.Position = new List<int>();
            foreach (int j in inputArray)
            {                    
                if (i == j)
                {
                    pair.Position.Add(index);
                }
                index++;
            }
            pair.Number = i;
            pairList[pairIndex] = pair;
            pairIndex++;
        }
        return pairList;
    }

Вот строка кода в методе Main

        Pair[] pairs = NewCollection.GetIndex(array, intQuery);

        List<int> minWindow = new List<int>();
        for (int i = 0; i <pairs.Length - 1; i++)
        {
            List<int> first = pairs[i].Position;
            List<int> second = pairs[i + 1].Position;
            int? temp = null;
            int? temp1 = null;
            foreach(int m in first)
            {
                foreach (int n in second)
                {
                    if (n > m)
                    {
                        temp = m;
                        temp1 = n;
                    }                        
                }                    
            }
            if (temp.HasValue && temp1.HasValue)
            {
                if (!minWindow.Contains((int)temp))
                    minWindow.Add((int)temp);
                if (!minWindow.Contains((int)temp1))
                    minWindow.Add((int)temp1);
            }
            else
            {
                Console.WriteLine(" Bad Query array");
                minWindow.Clear();
                break;                    
            }
        }

        if(minWindow.Count > 0)
        {
         Console.WriteLine("Minimum Window is :");
         foreach(int i in minWindow)
         {
             Console.WriteLine(i + " ");
         }
        }
0 голосов
/ 19 сентября 2010

Алгоритм:

  1. получить все индексы во входном массиве всех значений queryArray
  2. упорядочить их по возрастанию по индексу
  3. , используя каждый индекс (x) в качестве начальногонайти первый более высокий индекс (y) так, чтобы сегмент inputArray [xy] содержал все значения queryArray
  4. , оставляя только те сегменты, которые имеют элементы queryArray, в порядке
  5. , упорядочивая сегменты по их длине, по возрастанию

c # реализация:

Сначала получите все индексы во входном массиве всех значений queryArray и упорядочите их по возрастанию по индексу.

public static int[] SmallestWindow(int[] inputArray, int[] queryArray)
{
    var indexed = queryArray
        .SelectMany(x => inputArray
                             .Select((y, i) => new
                                 {
                                     Value = y,
                                     Index = i
                                 })
                             .Where(y => y.Value == x))
        .OrderBy(x => x.Index)
        .ToList();

Далее, используякаждый индекс (x) в качестве отправной точки находит первый более высокий индекс (y), так что сегмент inputArray [xy] содержит все значения queryArray.

    var segments = indexed
        .Select(x =>
            {
                var unique = new HashSet<int>();
                return new
                    {
                        Item = x,
                        Followers = indexed
                            .Where(y => y.Index >= x.Index)
                            .TakeWhile(y => unique.Count != queryArray.Length)
                            .Select(y =>
                                {
                                    unique.Add(y.Value);
                                    return y;
                                })
                            .ToList(),
                        IsComplete = unique.Count == queryArray.Length
                    };
            })
        .Where(x => x.IsComplete);

Теперь сохраняем только те сегменты, в которых есть элементы queryArray.order.

    var queryIndexed = segments
        .Select(x => x.Followers.Select(y => new
            {
                QIndex = Array.IndexOf(queryArray, y.Value),
                y.Index,
                y.Value
            }).ToArray());

    var queryOrdered = queryIndexed
        .Where(item =>
            {
                var qindex = item.Select(x => x.QIndex).ToList();
                bool changed;
                do
                {
                    changed = false;
                    for (int i = 1; i < qindex.Count; i++)
                    {
                        if (qindex[i] <= qindex[i - 1])
                        {
                            qindex.RemoveAt(i);
                            changed = true;
                        }
                    }
                } while (changed);
                return qindex.Count == queryArray.Length;
            });

Наконец, упорядочите сегменты по длине в порядке возрастания.Первый сегмент в результате - это самое маленькое окно в inputArray, которое содержит все значения queryArray в порядке queryArray.

    var result = queryOrdered
        .Select(x => new[]
            {
                x.First().Index,
                x.Last().Index
            })
        .OrderBy(x => x[1] - x[0]);

    var best = result.FirstOrDefault();
    return best;
}

проверить его с помощью

public void Test()
{
    var inputArray = new[] { 2, 1, 5, 6, 8, 1, 8, 6, 2, 9, 2, 9, 1, 2 };
    var queryArray = new[] { 6, 1, 2 };

    var result = SmallestWindow(inputArray, queryArray);

    if (result == null)
    {
        Console.WriteLine("no matching window");
    }
    else
    {
        Console.WriteLine("Smallest window is indexes " + result[0] + " to " + result[1]);
    }
}

output:

Smallest window is indexes 3 to 8
0 голосов
/ 19 сентября 2010

После того, как вы получили все позиции (индексы) в inputArray:

2 --> position {0,2}   // note: I change them to 0-based array
1 --> position {5,6}  // I suppose it's {5,6} to make it more complex, in your code it's only {5}
7 --> position {7}

Я использую рекурсию, чтобы найти все возможные пути. [0-> 5-> 7] [0-> 6-> 7] [2-> 5-> 7] [2-> 6-> 7]. Всего 2 * 2 * 1 = 4 возможных пути. Очевидно, что тот, кто имеет Min(Last-First), является самым коротким путем (самым маленьким окном), эти числа в середине пути не имеют значения. Вот код.

 struct Pair
 {
     public int Number;  // the number in queryArray
     public int[] Indexes;  // the positions of the number
 }
 static List<int[]> results = new List<int[]>(); //store all possible paths
 static Stack<int> currResult = new Stack<int>(); // the container of current path
 static int[] inputArray, queryArray; 
 static Pair[] pairs;

После структур данных вот Main.

inputArray = new int[] { 2, 7, 1, 5, 2, 8, 0, 1, 4, 7 }; //my test case
queryArray = new int[] { 2, 1, 7 };
pairs = (from n in queryArray
      select new Pair { Number = n, Indexes = inputArray.FindAllIndexes(i => i == n) }).ToArray();
Go(0);

FindAllIndexes - это метод расширения, помогающий найти все индексы.

public static int[] FindAllIndexes<T>(this IEnumerable<T> source, Func<T,bool> predicate)
{
     //do necessary check here, then
     Queue<int> indexes = new Queue<int>();
     for (int i = 0;i<source.Count();i++)
           if (predicate(source.ElementAt(i))) indexes.Enqueue(i);
     return indexes.ToArray();
}

Метод рекурсии:

static void Go(int depth)
{
    if (depth == pairs.Length)
    {
        results.Add(currResult.Reverse().ToArray());
    }
    else
    {
        var indexes = pairs[depth].Indexes;
        for (int i = 0; i < indexes.Length; i++)
        {
            if (depth == 0 || indexes[i] > currResult.Last())
            {
                currResult.Push(indexes[i]);
                Go(depth + 1);
                currResult.Pop();
            }
        }
    }
}

Наконец, цикл results может найти результат Min(Last-First) (самое короткое окно).

0 голосов
/ 19 сентября 2010

Вы хотите не HashSet, а (отсортированное) дерево или массив в качестве значения в словаре;словарь содержит сопоставления значений, которые вы найдете во входном массиве, с (отсортированным) списком индексов, в которых появляется это значение.

Затем выполните следующее

  • Просмотрите первую записьв запросе.Выберите самый низкий индекс, где он появляется.
  • Найдите вторую запись;выберите самую низкую запись, большую, чем индекс первой.
  • Найдите третью;выберите самый низкий больше, чем второй.(И т. Д.)
  • Когда вы достигнете последней записи в запросе, (1 + последний индекс - первый индекс) - это размер наименьшего совпадения.
  • Теперь выберите второй индекс изпервый запрос, повтор и т. д.
  • Выберите наименьшее совпадение, найденное по любому из начальных индексов.

(Обратите внимание, что «наименьшая запись больше» - это операция, предоставляемая с отсортированными деревьямиили может быть найден с помощью бинарного поиска в отсортированном массиве.)

Сложность этого составляет примерно O(M*n*log n), где M - длина запроса, а n - среднее число индексов вкоторое заданное значение появляется во входном массиве.Вы можете изменить стратегию, выбрав значение этого массива запросов, которое реже всего появляется для начальной точки, и затем подниматься и опускаться;если таких записей k (k <= <code>n), то сложность составляет O(M*k*log n).

0 голосов
/ 19 сентября 2010

Я не вижу, как использование HashSet и Dictionary поможет вам в этом. Если бы я столкнулся с этой проблемой, я бы пошел по-другому.

Один из способов сделать это (не самый эффективный) показан ниже. Этот код предполагает, что queryArray содержит как минимум два элемента.

int FindInArray(int[] a, int start, int value)
{
    for (int i = start; i < a.Length; ++i)
    {
        if (a[i] == value)
            return i;
    }
    return -1;
}

struct Pair
{
    int first;
    int last;
}

List<Pair> foundPairs = new List<Pair>();

int startPos = 0;
bool found = true;
while (found)
{
    found = false;
    // find next occurrence of queryArray[0] in inputArray
    startPos = FindInArray(inputArray, startPos, queryArray[0]);
    if (startPos == -1)
    {
        // no more occurrences of the first item
        break;
    }
    Pair p = new Pair();
    p.first = startPos;
    ++startPos;
    int nextPos = startPos;
    // now find occurrences of remaining items
    for (int i = 1; i < queryArray.Length; ++i)
    {
        nextPos = FindInArray(inputArray, nextPos, queryArray[i]);
        if (nextPos == -1)
        {
            break;  // didn't find it
        }
        else
        {
            p.last = nextPos++;
            found = (i == queryArray.Length-1);
        }
    }
    if (found)
    {
        foundPairs.Add(p);
    }
}

// At this point, the foundPairs list contains the (start, end) of all
// sublists that contain the items in order.
// You can then iterate through that list, subtract (last-first), and take
// the item that has the smallest value.  That will be the shortest sublist
// that matches the criteria.

С некоторой работой это можно сделать более эффективным. Например, если 'queryArray' содержит [1, 2, 3], а inputArray содержит [1, 7, 4, 9, 1, 3, 6, 4, 1, 8, 2, 3], вышеуказанный код найдет три совпадения (начиная с позиций 0, 4 и 8). Немного более умный код может определить, что когда 1 в позиции 4 найден, поскольку до него не было найдено 2, любая последовательность, начинающаяся в первой позиции, будет длиннее последовательности, начинающейся в позиции 4, и поэтому короткая -схема первой последовательности и начать все сначала. Однако это немного усложняет код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...