Получение ближайшего соответствия строк

Мне нужен способ сравнить несколько строк с тестовой строкой и вернуть строку, которая очень похожа на нее:

TEST STRING: THE BROWN FOX JUMPED OVER THE RED COW

CHOICE A   : THE RED COW JUMPED OVER THE GREEN CHICKEN
CHOICE B   : THE RED COW JUMPED OVER THE RED COW
CHOICE C   : THE RED FOX JUMPED OVER THE BROWN COW

(Если я сделал это правильно) Ближайшей строкой к "TEST STRING" должен быть "ВЫБОР C". Каков самый простой способ сделать это?

Я планирую реализовать это на нескольких языках, включая VB.net, Lua и JavaScript. На данный момент псевдокод является приемлемым. Если вы можете предоставить пример для определенного языка, это тоже оценено!

Ответ 1

Мне была представлена ​​эта проблема около года назад, когда дело дошло до поиска введенной пользователем информации о нефтяной вышке в базе данных различной информации. Цель состояла в том, чтобы выполнить какой-то поиск нечетких строк, который мог бы идентифицировать запись базы данных с наиболее распространенными элементами.

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

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

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


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

Введение

Необходимость выполнения сопоставления нечеткой строки первоначально возникла при разработке средства проверки Мексиканского залива Мексики. Существовала база данных известных нефтяных вышек и платформ Мексиканского залива, а люди, покупающие страховку, давали нам немного плохо напечатанную информацию об их активах, и нам приходилось сопоставлять ее с базой данных известных платформ. Когда было предоставлено очень мало информации, самое лучшее, что мы могли бы сделать, это полагаться на андеррайтера, чтобы "распознать" тот, на который они ссылались, и вызвать соответствующую информацию. Именно здесь это автоматическое решение пригодится.

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

Реализация

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

'Calculate the Levenshtein Distance between two strings (the number of insertions,
'deletions, and substitutions needed to transform the first string into the second)
Public Function LevenshteinDistance(ByRef S1 As String, ByVal S2 As String) As Long
    Dim L1 As Long, L2 As Long, D() As Long 'Length of input strings and distance matrix
    Dim i As Long, j As Long, cost As Long 'loop counters and cost of substitution for current letter
    Dim cI As Long, cD As Long, cS As Long 'cost of next Insertion, Deletion and Substitution
    L1 = Len(S1): L2 = Len(S2)
    ReDim D(0 To L1, 0 To L2)
    For i = 0 To L1: D(i, 0) = i: Next i
    For j = 0 To L2: D(0, j) = j: Next j

    For j = 1 To L2
        For i = 1 To L1
            cost = Abs(StrComp(Mid$(S1, i, 1), Mid$(S2, j, 1), vbTextCompare))
            cI = D(i - 1, j) + 1
            cD = D(i, j - 1) + 1
            cS = D(i - 1, j - 1) + cost
            If cI <= cD Then 'Insertion or Substitution
                If cI <= cS Then D(i, j) = cI Else D(i, j) = cS
            Else 'Deletion or Substitution
                If cD <= cS Then D(i, j) = cD Else D(i, j) = cS
            End If
        Next i
    Next j
    LevenshteinDistance = D(L1, L2)
End Function

Простая, быстрая и очень полезная метрика. Используя это, я создал две отдельные метрики для оценки сходства двух строк. Один из них я называю "valuePhrase", а другой - "valueWords". valuePhrase - это просто расстояние Левенштейна между двумя фразами, а valueWords разбивает строку на отдельные слова на основе разделителей, таких как пробелы, тире и все остальное, что вам нужно, и сравнивает каждое слово друг с другом, суммируя кратчайшие Левенштейн расстояние, соединяющее любые два слова. По существу, он измеряет, действительно ли информация в одной "фразе" содержится в другой, как перестановка слов. Я провел несколько дней в качестве побочного проекта, предлагающего наиболее эффективный способ разбиения строки на основе разделителей.

valueWords, функция valuePhrase и Split:

Public Function valuePhrase#(ByRef S1$, ByRef S2$)
    valuePhrase = LevenshteinDistance(S1, S2)
End Function

Public Function valueWords#(ByRef S1$, ByRef S2$)
    Dim wordsS1$(), wordsS2$()
    wordsS1 = SplitMultiDelims(S1, " _-")
    wordsS2 = SplitMultiDelims(S2, " _-")
    Dim word1%, word2%, thisD#, wordbest#
    Dim wordsTotal#
    For word1 = LBound(wordsS1) To UBound(wordsS1)
        wordbest = Len(S2)
        For word2 = LBound(wordsS2) To UBound(wordsS2)
            thisD = LevenshteinDistance(wordsS1(word1), wordsS2(word2))
            If thisD < wordbest Then wordbest = thisD
            If thisD = 0 Then GoTo foundbest
        Next word2
foundbest:
        wordsTotal = wordsTotal + wordbest
    Next word1
    valueWords = wordsTotal
End Function

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' SplitMultiDelims
' This function splits Text into an array of substrings, each substring
' delimited by any character in DelimChars. Only a single character
' may be a delimiter between two substrings, but DelimChars may
' contain any number of delimiter characters. It returns a single element
' array containing all of text if DelimChars is empty, or a 1 or greater
' element array if the Text is successfully split into substrings.
' If IgnoreConsecutiveDelimiters is true, empty array elements will not occur.
' If Limit greater than 0, the function will only split Text into 'Limit'
' array elements or less. The last element will contain the rest of Text.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function SplitMultiDelims(ByRef Text As String, ByRef DelimChars As String, _
        Optional ByVal IgnoreConsecutiveDelimiters As Boolean = False, _
        Optional ByVal Limit As Long = -1) As String()
    Dim ElemStart As Long, N As Long, M As Long, Elements As Long
    Dim lDelims As Long, lText As Long
    Dim Arr() As String

    lText = Len(Text)
    lDelims = Len(DelimChars)
    If lDelims = 0 Or lText = 0 Or Limit = 1 Then
        ReDim Arr(0 To 0)
        Arr(0) = Text
        SplitMultiDelims = Arr
        Exit Function
    End If
    ReDim Arr(0 To IIf(Limit = -1, lText - 1, Limit))

    Elements = 0: ElemStart = 1
    For N = 1 To lText
        If InStr(DelimChars, Mid(Text, N, 1)) Then
            Arr(Elements) = Mid(Text, ElemStart, N - ElemStart)
            If IgnoreConsecutiveDelimiters Then
                If Len(Arr(Elements)) > 0 Then Elements = Elements + 1
            Else
                Elements = Elements + 1
            End If
            ElemStart = N + 1
            If Elements + 1 = Limit Then Exit For
        End If
    Next N
    'Get the last token terminated by the end of the string into the array
    If ElemStart <= lText Then Arr(Elements) = Mid(Text, ElemStart)
    'Since the end of string counts as the terminating delimiter, if the last character
    'was also a delimiter, we treat the two as consecutive, and so ignore the last elemnent
    If IgnoreConsecutiveDelimiters Then If Len(Arr(Elements)) = 0 Then Elements = Elements - 1

    ReDim Preserve Arr(0 To Elements) 'Chop off unused array elements
    SplitMultiDelims = Arr
End Function

Меры сходства

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

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

Fuzzy String Matching Permutations

В приведенном выше скриншоте я подкрепил свою эвристику, чтобы придумать что-то, что я почувствовал, насколько хорошо я понял свою воспринимаемую разницу между поисковым термином и результатом. Эвристика, которую я использовал для Value Phrase в приведенной выше таблице, была =valuePhrase(A2,B2)-0.8*ABS(LEN(B2)-LEN(A2)). Я эффективно уменьшал штраф на расстояние Левенштейна на 80% от разницы в длине двух "фраз". Таким образом, "фразы", ​​которые имеют одинаковую длину, страдают от полного штрафа, но "фразы", ​​содержащие "дополнительную информацию" (дольше), но помимо того, что они по большей части разделяют одни и те же персонажи, подвергаются уменьшенному штрафу. Я использовал функцию Value Words как есть, и тогда моя окончательная эвристика SearchVal была определена как =MIN(D2,E2)*0.8+MAX(D2,E2)*0.2 - средневзвешенное значение. Какой из двух оценок был ниже, он получил 80% и 20% от более высокого балла. Это была всего лишь эвристика, которая соответствовала моему варианту использования, чтобы получить хорошую скорость матча. Эти веса - это то, что можно было бы затем настроить, чтобы получить наилучший коэффициент соответствия с их тестовыми данными.

Fuzzy String Matching Value Phrase

Fuzzy String Matching Value Words

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

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

value = Min(phraseWeight*phraseValue, wordsWeight*wordsValue)*minWeight
      + Max(phraseWeight*phraseValue, wordsWeight*wordsValue)*maxWeight
      + lengthWeight*lengthValue

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

Fuzzy String Matching Optimized Metric

Алгоритм был замечательным успехом, и параметры решения говорят об этом типе проблемы. Вы заметите, что оптимизированный балл составлял 44, а наилучший результат - 48. 5 столбцов в конце являются приманками и не имеют никакого значения для значений строк. Чем больше приманки, тем труднее будет найти наилучшее совпадение.

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

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

Наконец, минимальный вес оптимизирован на уровне 10 и максимальный вес в 1. Что это означает, что если лучший из двух оценок (фразу значения и слова значения) не очень хорош, матч сильно наказывается, но мы не очень наказываем худшее из двух баллов. По сути, это ставит акцент на том, чтобы либо valueWord, либо valuePhrase имели хороший балл, но не оба. Какой-то "взять то, что мы можем получить".

Это действительно захватывает то, что оптимизированное значение этих 5 весов говорит о том, что происходит нечеткое соответствие строк. Для совершенно разных практических случаев совпадения нечетких строк эти параметры очень разные. Я использовал его для 3 отдельных приложений.

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

Fuzzy String Matching Benchmark

Другие приложения

Это решение может использоваться везде, где пользователь хочет, чтобы компьютерная система идентифицировала строку в наборе строк, где нет идеального соответствия. (Как примерное совпадение vlookup для строк).


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

С помощью соответствующего набора эвристик и весов вы сможете быстро сравнить свои программы сравнения с принятыми вами решениями.

Ответ 2

Эта проблема все время появляется в биоинформатике. Принятый ответ выше (что было замечательно, кстати) известен в биоинформатике как Needleman-Wunsch (сравнить две строки) и Smith-Waterman (найти приближенную подстроку в более длинной последовательности) алгоритмов. Они отлично работают и работали на протяжении десятилетий.

Но что, если у вас есть миллион строк для сравнения?. Это триллион парных сравнений, каждый из которых - O (n * m)! Современные ДНК-секвенсоры легко генерируют миллиард коротких последовательностей ДНК, каждый из которых содержит около 200 ДНК "букв". Как правило, мы хотим найти для каждой такой строки наилучшее совпадение с геномом человека (3 миллиарда букв). Очевидно, что алгоритм Needleman-Wunsch и его родственники не будут делать.

Эта так называемая "проблема выравнивания" - область активных исследований. Самые популярные алгоритмы в настоящее время способны находить неточные совпадения между 1 миллиардом коротких строк и геномом человека в течение нескольких часов на разумных аппаратных средствах (например, восемь ядер и 32 ГБ оперативной памяти).

Большинство этих алгоритмов работают, быстро находят короткие точные соответствия (семена), а затем расширяют их до полной строки с использованием более медленного алгоритма (например, Smith-Waterman). Причина этого в том, что нас действительно интересуют только несколько близких совпадений, поэтому он рассчитывает избавиться от 99,9...% пар, которые не имеют ничего общего.

Как найти точные соответствия помогают найти неточные соответствия? Ну, скажем, мы разрешаем только одно различие между запросом и целью. Легко видеть, что это различие должно происходить либо в правой, либо в левой половине запроса, поэтому другая половина должна точно соответствовать. Эта идея может быть расширена до нескольких несоответствий и является основой для алгоритма ELAND, который обычно используется с секвенсерами ДНК Illumina.

Существует много очень хороших алгоритмов для точного соответствия строк. Учитывая строку запроса длиной 200 и целевую строку длиной 3 миллиарда (человеческий геном), мы хотим найти любое место в целевой точке, где есть подстрока длиной k, которая точно соответствует подстроке запроса. Простой подход - начать с индексирования цели: взять все подстроки k-long, поместить их в массив и отсортировать. Затем возьмите каждую подстроку k-long запроса и выполните поиск отсортированного индекса. Сортировка и поиск можно выполнить в режиме O (log n).

Но память может быть проблемой. Индекс целевой цели в 3 миллиарда должен будет содержать 3 миллиарда указателей и 3 миллиарда k-слов. Казалось бы, трудно подогнать это менее чем в несколько десятков гигабайт оперативной памяти. Но удивительно, что мы можем значительно сжать индекс, используя преобразование Burrows-Wheeler, и оно будет по-прежнему эффективно запрашиваться. Индекс генома человека может вместить менее 4 ГБ оперативной памяти. Эта идея является основой популярных последователей последовательности, таких как Bowtie и BWA.

В качестве альтернативы мы можем использовать массив сущностей , который хранит только указатели, но представляет собой одновременный индекс всех суффиксов в целевой строке (по существу, одновременный индекс для всех возможных значений k, то же самое верно и для преобразования Берроуза-Уилера). Индекс массива суффиксов генома человека будет занимать 12 ГБ ОЗУ, если мы будем использовать 32-битные указатели.

В приведенных выше ссылках содержится большое количество информации и ссылок на основные научные статьи. Ссылка ELAND идет в PDF с полезными цифрами, иллюстрирующими вовлеченные концепции, и показывает, как бороться со вставками и удалениями.

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

Ответ 3

Я оспариваю, что выбор B ближе к тестовой строке, так как только 4 символа (и 2 удаления) являются исходной строкой. В то время как вы видите C ближе, поскольку он включает в себя как коричневый, так и красный. Однако он имел бы большее расстояние редактирования.

Существует алгоритм под названием Levenshtein Distance, который измеряет расстояние редактирования между двумя входами.

Здесь является инструментом для этого алгоритма.

  • Выбор ставок A как расстояние 15.
  • Выбор ставок B как расстояние до 6.
  • Выбор тарифов C как расстояние до 9.

EDIT: Извините, я продолжаю смешивать строки в инструменте levenshtein. Обновлено для правильных ответов.

Ответ 4

Реализация Lua для потомков:

function levenshtein_distance(str1, str2)
    local len1, len2 = #str1, #str2
    local char1, char2, distance = {}, {}, {}
    str1:gsub('.', function (c) table.insert(char1, c) end)
    str2:gsub('.', function (c) table.insert(char2, c) end)
    for i = 0, len1 do distance[i] = {} end
    for i = 0, len1 do distance[i][0] = i end
    for i = 0, len2 do distance[0][i] = i end
    for i = 1, len1 do
        for j = 1, len2 do
            distance[i][j] = math.min(
                distance[i-1][j  ] + 1,
                distance[i  ][j-1] + 1,
                distance[i-1][j-1] + (char1[i] == char2[j] and 0 or 1)
                )
        end
    end
    return distance[len1][len2]
end

Ответ 5

Вам может быть интересно это сообщение в блоге.

http://seatgeek.com/blog/dev/fuzzywuzzy-fuzzy-string-matching-in-python

Fuzzywuzzy - это библиотека Python, которая обеспечивает легкие дистанционные измерения, такие как расстояние Левенштейна для сопоставления строк. Он построен поверх библиотеки difflib в стандартной библиотеке и будет использовать реализацию C Python-levenshtein, если она доступна.

http://pypi.python.org/pypi/python-Levenshtein/

Ответ 6

Вы можете найти эту библиотеку полезной! http://code.google.com/p/google-diff-match-patch/

В настоящее время он доступен в Java, JavaScript, Dart, С++, С#, Objective C, Lua и Python

Это тоже очень хорошо. Я использую его в нескольких проектах Lua.

И я не думаю, что было бы слишком сложно перенести его на другие языки!

Ответ 7

Если вы делаете это в контексте поисковой системы или интерфейса с базой данных, вы можете использовать такой инструмент, как Apache Solr, с плагином ComplexPhraseQueryParser. Эта комбинация позволяет вам искать по индексу строк с результатами, отсортированными по релевантности, как определено расстоянием Левенштейна.

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

Кроме того, с помощью Solr вы можете искать по индексу по требованию через JSON, поэтому вам не придется повторно изобретать решение между различными языками, которые вы ищете.

Ответ 8

Очень, очень хороший ресурс для этих видов алгоритмов - Simmetrics: http://sourceforge.net/projects/simmetrics/

К сожалению, ужасный веб-сайт, содержащий много документации, ушел:( В случае, если он возвращается снова, его предыдущий адрес был следующим: http://www.dcs.shef.ac.uk/~sam/simmetrics.html

Voila (любезно предоставлена ​​ "Wayback Machine" ): http://web.archive.org/web/20081230184321/http://www.dcs.shef.ac.uk/~sam/simmetrics.html

Вы можете изучить источник кода, есть десятки алгоритмов для этих сравнений, каждый из которых имеет разные компромиссы. Реализации находятся на Java.

Ответ 9

Для эффективного запроса большого набора текста вы можете использовать концепцию Edit Distance/Prefix Edit Distance.

Изменить расстояние ED (x, y): минимальное количество трансфромсов для перехода от члена x к члену y

Но вычисление ED между каждым термином и текстом запроса является ресурсоемким и интенсивным. Поэтому вместо вычисления ED для каждого термина сначала мы можем извлечь возможные совпадающие термины, используя метод, называемый индексом Qgram. а затем применить вычисление ЭД на этих выбранных условиях.

Преимущество метода индекса Qgram заключается в поддержке Fuzzy Search.

Одним из возможных подходов к адаптации индекса QGram является построение Инвертированного индекса с использованием Qgrams. Там мы сохраняем все слова, которые состоят из определенного Qgram, под этим Qgram. (Вместо хранения полной строки вы можете использовать уникальный идентификатор для каждой строки). Для этого вы можете использовать структуру данных Tree Map для Java. Ниже приведен небольшой пример сохранения терминов

col: col mbia, col ombo, gan col a, ta col ama

Затем при запросе мы вычисляем количество общих Qgrams между текстом запроса и доступными терминами.

Example: x = HILLARY, y = HILARI(query term)
Qgrams
$$HILLARY$$ -> $$H, $HI, HIL, ILL, LLA, LAR, ARY, RY$, Y$$
$$HILARI$$ -> $$H, $HI, HIL, ILA, LAR, ARI, RI$, I$$
number of q-grams in common = 4

число общих q-граммов = 4.

Для терминов с большим количеством общих Qgrams мы вычисляем ED/PED в соответствии с термином запроса, а затем предлагаем термин для конечного пользователя.

вы можете найти реализацию этой теории в следующем проекте (см. "QGramIndex.java" ). Не стесняйтесь задавать любые вопросы. https://github.com/Bhashitha-Gamage/City_Search

Чтобы узнать больше об изменении расстояния, префикса Edit Distance Qgram index, пожалуйста, просмотрите следующее видео профессора д-ра Ханны Баст https://www.youtube.com/embed/6pUg2wmGJRo ( Занятие начинается с 20:06)

Ответ 10

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

var res = client.Search<ClassName>(s => s
    .Query(q => q
    .Match(m => m
        .Field(f => f.VariableName)
        .Query("SAMPLE QUERY")
        .Fuzziness(Fuzziness.EditDistance(5))
    )
));