Недостаточно памяти для списка строк

Я пытаюсь решить задачу в LeetCode, и кажется, она почти решена. Вот что я сделал на данный момент: Скрипка и проблемная ссылка — 30. Подстрока с объединением всех слов. Ниже приведен фрагмент кода, который я сделал на данный момент:

static IList < int > FindSubstring(string s, string[] words) {
    var list = new List < IList < string >> ();
    DoPermutation(words, 0, words.Length - 1, list);

    return TrackIndexes(s, list);
  }

  static IList < IList < string >> DoPermutation(string[] str, int start, int end, IList < IList < string >> list) {
    if (start == end) {
      list.Add(new List < string > (str));
    } else {
      for (var i = start; i <= end; i++) {
        Swap(ref str[start], ref str[i]);

        DoPermutation(str, start + 1, end, list);

        Swap(ref str[start], ref str[i]);
      }
    }

    return list;
  }

  static void Swap(ref string a, ref string b) {
    var temp = a;
    a = b;
    b = temp;
  }

  static List < int > TrackIndexes(string pattern, IList < IList < string >> aLst) {
    int count = 0;

    List < string > strings;
    List < int > indexes = new List < int > ();

    foreach(var item in aLst) {
      string str = "";

      strings = new List < string > {
        String.Concat(item)
      };

      foreach(var list in item) {
        str += list;

        if (str.Length == strings[0].Length && pattern.Contains(str)) {
          indexes.AddRange(AllIndexesOf(pattern, str));
          count += 1;
        }
      }
    }

    indexes.Sort();
    return indexes.Distinct().ToList();
  }

public static int[] AllIndexesOf(string str, string substr, bool ignoreCase = false) 
{
  if (string.IsNullOrWhiteSpace(str) || string.IsNullOrWhiteSpace(substr)) 
  {
    throw new ArgumentException("String or substring is not specified.");
  }

  var indexes = new List<int>();
  int index = 0;

  while ((index = str.IndexOf(substr, index, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) != -1) 
  {
    indexes.Add(index++);
  }

  return indexes.ToArray();
}

У меня возникла проблема со следующим вводом:

s = "pjzkrkevzztxductzzxmxsvwjkxpvukmfjywwetvfnujhweiybwvvsrfequzkhossmootkmyxgjgfordrpapjuunmqnxxdrqrfgkrsjqbszgiqlcfnrpjlcwdrvbumtotzylshdvccdmsqoadfrpsvnwpizlwszrtyclhgilklydbmfhuywotjmktnwrfvizvnmfvvqfiokkdprznnnjycttprkxpuykhmpchiksyucbmtabiqkisgbhxngmhezrrqvayfsxauampdpxtafniiwfvdufhtwajrbkxtjzqjnfocdhekumttuqwovfjrgulhekcpjszyynadxhnttgmnxkduqmmyhzfnjhducesctufqbumxbamalqudeibljgbspeotkgvddcwgxidaiqcvgwykhbysjzlzfbupkqunuqtraxrlptivshhbihtsigtpipguhbhctcvubnhqipncyxfjebdnjyetnlnvmuxhzsdahkrscewabejifmxombiamxvauuitoltyymsarqcuuoezcbqpdaprxmsrickwpgwpsoplhugbikbkotzrtqkscekkgwjycfnvwfgdzogjzjvpcvixnsqsxacfwndzvrwrycwxrcismdhqapoojegggkocyrdtkzmiekhxoppctytvphjynrhtcvxcobxbcjjivtfjiwmduhzjokkbctweqtigwfhzorjlkpuuliaipbtfldinyetoybvugevwvhhhweejogrghllsouipabfafcxnhukcbtmxzshoyyufjhzadhrelweszbfgwpkzlwxkogyogutscvuhcllphshivnoteztpxsaoaacgxyaztuixhunrowzljqfqrahosheukhahhbiaxqzfmmwcjxountkevsvpbzjnilwpoermxrtlfroqoclexxisrdhvfsindffslyekrzwzqkpeocilatftymodgztjgybtyheqgcpwogdcjlnlesefgvimwbxcbzvaibspdjnrpqtyeilkcspknyylbwndvkffmzuriilxagyerjptbgeqgebiaqnvdubrtxibhvakcyotkfonmseszhczapxdlauexehhaireihxsplgdgmxfvaevrbadbwjbdrkfbbjjkgcztkcbwagtcnrtqryuqixtzhaakjlurnumzyovawrcjiwabuwretmdamfkxrgqgcdgbrdbnugzecbgyxxdqmisaqcyjkqrntxqmdrczxbebemcblftxplafnyoxqimkhcykwamvdsxjezkpgdpvopddptdfbprjustquhlazkjfluxrzopqdstulybnqvyknrchbphcarknnhhovweaqawdyxsqsqahkepluypwrzjegqtdoxfgzdkydeoxvrfhxusrujnmjzqrrlxglcmkiykldbiasnhrjbjekystzilrwkzhontwmehrfsrzfaqrbbxncphbzuuxeteshyrveamjsfiaharkcqxefghgceeixkdgkuboupxnwhnfigpkwnqdvzlydpidcljmflbccarbiegsmweklwngvygbqpescpeichmfidgsjmkvkofvkuehsmkkbocgejoiqcnafvuokelwuqsgkyoekaroptuvekfvmtxtqshcwsztkrzwrpabqrrhnlerxjojemcxel", 

words = ["dhvf","sind","ffsl","yekr","zwzq","kpeo","cila","tfty","modg","ztjg","ybty","heqg","cpwo","gdcj","lnle","sefg","vimw","bxcb"]

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

На данный момент мой код проходит 151 тест из 180. Можно ли в любом случае использовать существующий фрагмент кода, чтобы избавиться от исключения памяти, или мне придется переосмыслить проблему?

Цель: Основная цель кода — создать возможную перестановку для списка строк и сопоставить эту строку с шаблоном как подстроку.

Вам действительно нужно создать полностью материализованные List<List<string>> перестановки? Или вы могли бы просто создать перечислимое IEnumerable<List<string>>, в котором одновременно материализуется только один список?

dbc 02.05.2024 19:24

Как я указал в своем комментарии к вашему предыдущему вопросу, который вы удалили, ваш текущий алгоритм создает огромное количество перестановок: 6,402,373,705,728,000 всего для 18 слов. Даже если вы избежите выделения памяти, для завершения выполнения потребуется несколько дней. Вам нужно найти что-то с меньшей алгоритмической сложностью.

StriplingWarrior 03.05.2024 06:43
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
131
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Теперь ты заставил меня действительно решить головоломку для фанатов. Хотя ваше решение может работать при некоторой оптимизации, я думаю, вам следует отойти от текущей идеи и переосмыслить проблему. Попробуйте еще раз объяснить себе проблему.

Подсказка: использование слова «перестановка» — это своего рода приманка, которая заставит вас создать этот список. Попробуйте выразить проблему без этого употребления.

Подсказка с небольшими спойлерами

Я решил это, используя эти имена и сигнатуры методов:

public IList<int> FindSubstring(string s, string[] words)

Dictionary<string, int> ExtractWordOccurances(string candidateSubstring, int chopSize)

bool SubStringMeetsRequirements(Dictionary<string, int> extractedChunks, string[] words)

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

Да, конечно, я бы не стал отвечать, если бы решение было неверным или если бы я не был в нем уверен. Хотя, как я уже говорил, это неэффективно.

Glubus 05.05.2024 01:42

Ваша проблема в том, что вы материализуете список всех возможных перестановок ваших слов. Однако в этом нет необходимости. Все, что вам нужно сделать, это перечислить возможные перестановки и проверить каждую из них на какое-то условие. в C# это можно сделать, реализовав метод IEnumerable<string []>, который материализует только один строковый массив за раз.

Ниже приведен один из таких примеров, написанный для .NET 8/C# 12:

public static class EnumerableExtensions
{
    ///<summary>
    ///Enumerate the possible permutations of the incoming array.  
    ///Note that the memory of the returned array is reused for the next permutation
    ///</summary>
    public static IEnumerable<T []> EnumeratePermutations<T>(this T [] array)
    {
        if (array.Length == 0)
        {
            yield return array;
            yield break;
        }
        
        // Mutate a copy rather than the original
        var copy = array.ToArray();
        
        static IEnumerable<T []> EnumeratePermutations(T [] array, int start)
        {
            if (start >= array.Length)
                throw new ArgumentException("start >= array.Length");
            for (int i = start; i < array.Length; i++)
            {
                Swap(ref array[start], ref array[i]);
                yield return array;
                Swap(ref array[start], ref array[i]);
            }
        }

        var stack = new Stack<IEnumerator<T []>>();
        try
        {
            stack.Push(EnumeratePermutations(copy, 0).GetEnumerator());
            while (stack.Count != 0)
            {
                var enumerator = stack.Peek();
                if (!enumerator.MoveNext())
                    stack.Pop().Dispose();
                else if (stack.Count < copy.Length)
                    stack.Push(EnumeratePermutations(enumerator.Current, stack.Count).GetEnumerator());
                else
                    yield return enumerator.Current;
            }
        }
        finally
        {
            foreach (var enumerator in stack)
                enumerator.Dispose();
        }
    }
    
    static void Swap<T>(ref T a, ref T b) 
    {
        var temp = a;
        a = b;
        b = temp;
    }
}

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

Затем вы можете изменить свой TrackIndexes(), чтобы он принимал аргумент IEnumerable<string []> aLst, и действовать, как раньше:

static List<int> TrackIndexes(string pattern, IEnumerable<string []> aLst) {
  // TODO: write and test this yourself
}

При этом, хотя это решит вашу проблему нехватки памяти, оно не пройдет 30. Подстрока с объединением всех слов . Простое создание каждой перестановки с последующей проверкой того, содержится ли конкатенация в целевой строке, займет O(s.Length * Factorial(words.Length)), что будет недостаточно быстро, чтобы пройти тест. Вам придется значительно сократить количество переборов, которые вы перечисляете, сделав несколько быстрых отклонений, или переключиться на альтернативный подход, как предложено Глубусом в их ответе.

Ответ принят как подходящий

Поскольку все строки в массиве words имеют одинаковую длину, а максимальная длина (30) довольно мала, мы можем попробовать каждый возможный класс эквивалентности с длиной одного слова в качестве модуля. Для каждого из них мы можем использовать скользящее окно, чтобы отслеживать наибольшую совпадающую подстроку в O(|s|) (где |s| обозначает длину s).

Общая временная сложность: O(|w| * |s|) (где w представляет собой одно слово). Это также эквивалентно O(30 * |s|), поскольку максимальная длина одного слова составляет 30 символов.

public IList<int> FindSubstring(string s, string[] words) {
    int wlen = words[0].Length;
    var freq = words.GroupBy(x => x).ToDictionary(x => x.Key, x => x.Count());
    var res = new List<int>();
    for (int i = 0; i < wlen; i++) {
        var curr = new Dictionary<string, int>();
        for (int l = i, r = l; r + wlen <= s.Length; r += wlen) {
            var w = s.Substring(r, wlen);
            curr.TryGetValue(w, out var currCnt);
            curr[w] = currCnt + 1;
            for (; curr[w] > freq.GetValueOrDefault(w, 0); l += wlen)
                --curr[s.Substring(l, wlen)];
            if (r - l == (words.Length - 1) * wlen)
                res.Add(l);
        }
    }
    return res;
}

Другие вопросы по теме