Nano Hash - криптовалюты, майнинг, программирование

Как найти все строки братства?

У меня есть строка и еще один текстовый файл, содержащий список строк.

Мы называем 2 строки «строками братства», когда они полностью совпадают после сортировки по алфавиту.

Например, «abc» и «cba» будут отсортированы в «abc» и «abc», поэтому исходные два являются братскими. А вот "abc" и "aaa" - нет.

Итак, есть ли эффективный способ выбрать все строки братства из текстового файла в соответствии с одной предоставленной строкой?

Например, у нас есть "abc" и текстовый файл, в котором написано так:

abc
cba
acb
lalala

тогда "abc", "cba", "acb" являются ответами.

Конечно, «сортировать и сравнивать» — хорошая попытка, но под «эффективностью» я подразумеваю, если есть способ, мы можем определить, является ли строка-кандидат братством исходной после обработки за один проход.

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


  • что вы подразумеваете под эффективным - время, пространство (память), усилия по программированию? 24.09.2009
  • @Peter В этом вопросе лучше быстрее, чем меньше места, если вам приходится балансировать между временем и пространством. 24.09.2009
  • строки братства == анаграммы, которые отлично гуглятся... 24.09.2009

Ответы:


1

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

  • Настройте хеш-таблицу для исходной строки. Пусть каждая буква будет ключом, а количество раз, которое буква появляется в строке, будет значением. Назовите эту хеш-таблицу inputStringTable.
  • Разберите входную строку и каждый раз, когда вы видите символ, увеличивайте значение хеш-записи на единицу.
  • для каждой строки в файле
  • создать новую хеш-таблицу. Назовите это BrotherStringTable.
  • для каждого символа в строке добавить единицу в новую хеш-таблицу. Если BrotherStringTable[character] > inputStringTable[character], эта строка не является братом (один символ появляется слишком много раз)
  • после анализа строки сравните каждое значение inputStringTable с соответствующим значением BrotherStringTable. Если один отличается, то эта строка не является братской строкой. Если все совпадают, то строка является строкой-братом.

Это будет O(nk), где n — длина входной строки (любые строки длиннее входной строки могут быть немедленно отброшены), а k — количество строк в файле. Любой алгоритм на основе сортировки будет O(nk lg n), поэтому в некоторых случаях этот алгоритм работает быстрее, чем алгоритм на основе сортировки.

24.09.2009
  • Должно быть O(N) - у вас есть N O(1) приращений в хеш-таблице, а затем 2N O(1) обращений для сравнения хэшей. Однако постоянный коэффициент будет намного выше, чем мой ответ. 25.09.2009
  • Извините, я должен был сказать O(n*k), где n — длина исходной строки, а k — количество элементов в файле. Отредактировано. 25.09.2009

  • 2

    Сортировка каждой строки с последующим ее сравнением дает что-то вроде O(N*(k+log S)), где N — количество строк, k — длина ключа поиска, а S — средняя длина строки.

    Кажется, что подсчет вхождений каждого символа может быть здесь возможным способом (при условии, что строки имеют разумную длину). Это дает вам O(k+N*S). Будет ли это на самом деле быстрее, чем сортировка и сравнение, очевидно, будет зависеть от значений k, N и S.

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

    24.09.2009
  • как получается O(N*(k+log S))? Почему не O(N*(SlogS+k))? 27.09.2009

  • 3

    повторять, сортировать, сравнивать. это не должно быть слишком сложно, верно?

    24.09.2009
  • К сожалению, вам придется сортировать каждую строку таким образом, что кажется больше работы, чем вам нужно сделать, чтобы решить эту проблему... 24.09.2009
  • ну, вы можете поместить уже выбранные значения в хеш-таблицу и сначала проверить там 24.09.2009
  • или сначала создайте все возможные перестановки, а затем проверьте существование каждого значения. 24.09.2009
  • сортировка и сравнение занимают слишком много времени, поэтому проверка всех перестановок. хеш-таблица — это хороший вопрос, так что ключ в том, где хеш-функция? 24.09.2009
  • слово из 10 букв будет иметь более трех миллионов перестановок. если бы в файле было всего 100 слов, это могло бы быть излишним :) 24.09.2009
  • конечно Питер, поэтому он третий в списке предложений ;) 24.09.2009

  • 4

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

    Затем вы просматриваете набор строк, которые вы ищете, и перебираете символы в каждой строке. Вы можете уменьшить счетчик, связанный с каждой буквой в (копии) массива счетчиков из ключевой строки.

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

    Это позволяет пропустить сортировку в пользу копирования массива с постоянным временем и одной итерации по каждой строке.

    EDIT: После дальнейшего размышления это эффективно сортирует символы первой строки с использованием групповая сортировка.

    24.09.2009

    5

    Я думаю, что вам поможет тест, являются ли две строки анаграммами. Вот как вы можете это сделать. Я предполагаю, что на данный момент строка может содержать 256 символов ascii.

    #define NUM_ALPHABETS 256
    int alphabets[NUM_ALPHABETS];
    
    bool isAnagram(char *src, char *dest) {
        len1 = strlen(src);
        len2 = strlen(dest);
        if (len1 != len2)
            return false;
    
        memset(alphabets, 0, sizeof(alphabets));
        for (i = 0; i < len1; i++)
            alphabets[src[i]]++;
        for (i = 0; i < len2; i++) {
            alphabets[dest[i]]--;
            if (alphabets[dest[i]] < 0)
                return false;
        }
    
       return true;
    }
    

    Это будет работать за O (mn), если у вас есть строки «m» в файле средней длины «n».

    24.09.2009

    6
    • Отсортируйте строку запроса
    • Iterate through the Collection, doing the following:
      • Sort current string
      • Сравните со строкой запроса
      • Если он совпадает, это матч "братства", сохраните его/индекс/что хотите

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

    24.09.2009

    7

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

    Шаги будут такими:

    • построить гистограмму тестовой строки (обнулить массив int histogram[128] и увеличить позицию для каждого символа в тестовой строке)
    • для каждой входной строки

      • для каждого символа во входной строке c проверьте, равен ли histogram[c] нулю. Если да, то это не совпадение и восстановить гистограмму.

        • decrement histogram[c]
      • чтобы восстановить гистограмму, переместите входную строку обратно к ее началу, увеличивая, а не уменьшая

    В лучшем случае требуется два увеличения/уменьшения массива для каждого символа на входе.

    24.09.2009

    8

    Наиболее эффективный ответ будет зависеть от содержимого файла. Любой алгоритм, который мы придумаем, будет иметь сложность, пропорциональную N (количество слов в файле), L (средняя длина строк) и, возможно, V (разнообразие длин строк).

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

    target = sort_characters("target string")
    count = 0
    foreach (word in inputfile){
       if target.len == word.len && target == sort_characters(word){
          count++
        }
     }
    
    24.09.2009

    9

    Я бы рекомендовал:
    для каждой строки в текстовом файле:

    1. сравнить размер с «исходной строкой» (размер строк братства должен быть одинаковым)
    2. сравните хэши (CRC или хэш фреймворка по умолчанию должны быть хорошими)
    3. в случае справедливости сделайте более точное сравнение с отсортированными строками.

    Это не самый быстрый алгоритм, но он будет работать для любого алфавита/кодировки.

    25.09.2009

    10

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

    Назначьте разные простые числа для каждой буквы:

    prime['a']=2;
    prime['b']=3;
    prime['c']=5;
    

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

    long long key(char *string)
    {
      long long product=1;
      while (*string++) {
        product *= prime[*string];
      }
      return product;
    }
    

    Эта функция возвращает гарантированно уникальное целое число для любого набора букв, независимо от порядка их появления в строке. Получив значение для «ключа», вы можете пройтись по списку совпадающих строк и выполнить ту же операцию.

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

    25.09.2009

    11

    Вот реализация. Он создает словарь букв мастера, а строковая версия того же, что и сравнения строк, будет выполняться со скоростью C++. При создании словаря букв в пробной строке он сверяется с основным словарем, чтобы сбой в первый возможный момент - если он находит букву, не соответствующую оригиналу, или больше этой буквы, чем оригинал, он не работает . Вы можете заменить строки хэшами на основе целых чисел (согласно одному ответу относительно базы 26), если это окажется быстрее. В настоящее время хеш для сравнения выглядит как a3c2b1 для abacca.

    Это должно сработать O(N log( min(M,K)) ) для N строк длины M и ссылочной строки длины K и требует минимального количества поисков пробной строки.

    master = "abc"
    wordset = "def cba accb aepojpaohge abd bac ajghe aegage abc".split()
    
    def dictmaster(str):
        charmap = {}
        for char in str:
            if char not in charmap:
                charmap[char]=1
            else:
                charmap[char] += 1
        return charmap
    
    def dicttrial(str,mastermap):
        trialmap = {}
        for char in str:
            if char in mastermap:
                # check if this means there are more incidences
                # than in the master
                if char not in trialmap:
                    trialmap[char]=1
                else:
                    trialmap[char] += 1
            else:
                return False
        return trialmap
    
    def dicttostring(hash):
        if hash==False:
            return False
        str = ""
        for char in hash:
            str += char + `hash[char]`
        return str
    
    def testtrial(str,master,mastermap,masterhashstring):
        if len(master) != len(str):
            return False
        trialhashstring=dicttostring(dicttrial(str,mastermap))
        if (trialhashstring==False) or (trialhashstring != masterhashstring):
            return False
        else:
            return True
    
    mastermap = dictmaster(master)
    masterhashstring = dicttostring(mastermap)
    for word in wordset:
        if testtrial(word,master,mastermap,masterhashstring):
            print word+"\n"
    
    25.09.2009
    Новые материалы

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

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..