Lua - Как найти подстроку с расхождением в 1 или 2 символа

Скажем, у меня есть строка

local a = "Hello universe"

Я нахожу подстроку "вселенная" по

a:find("universe")

Теперь предположим, что строка

local a = "un#verse"

Строка для поиска — юниверс; но подстрока отличается одним символом. Так что очевидно, что Lua игнорирует это.

Как заставить функцию находить строку, даже если есть расхождение в один символ?


person SatheeshJM    schedule 19.10.2012    source источник
comment
Ознакомьтесь с алгоритмом расстояния Левенштейна.   -  person Mud    schedule 19.10.2012


Ответы (4)


Если вы знаете, где должен быть символ, используйте . вместо этого символа: a:find("un.verse")

Однако похоже, что вы ищете поиск нечеткой строки. Это выходит за рамки библиотеки Lua string. Вы можете начать с этой статьи: http://ntz-develop.blogspot.com/2011/03/fuzzy-string-search.html

Что касается реализации нечеткого поиска Lua — я не использовал их, но гугление «lua fuzzy search» дает несколько результатов. Некоторые из них основаны на этом документе: http://web.archive.org/web/20070518080535/http://www.heise.de/ct/english/97/04/386/

Попробуйте https://github.com/ajsher/luafuzzy.

person Alexander Gladysh    schedule 19.10.2012
comment
Ип! Я не осознавал, что это сложная проблема... Конечно, не стоит реализовывать это для моего текущего требования, которое очень тривиально.. Хотя это выглядит как очень интересная тема... может когда-нибудь пригодиться :) Спасибо за ответ! - person SatheeshJM; 19.10.2012

Похоже, вам нужно что-то вроде TRE:

TRE — это легкая, надежная и эффективная библиотека сопоставления регулярных выражений, совместимая с POSIX, с некоторыми интересными функциями, такими как приблизительное (нечеткое) сопоставление.

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

Привязка Lua для него доступна как часть lrexlib.

person furq    schedule 19.10.2012
comment
Аккуратная маленькая библиотека. К сожалению, я не могу интегрировать библиотеки C в свою программу. Так это из окна :( - person SatheeshJM; 19.10.2012

Если вы действительно ищете разницу в одном символе и не заботитесь о производительности, вот простой подход, который должен работать:

local a = "Hello un#verse"

local myfind = function(s,p)  
  local withdot = function(n)
    return p:sub(1,n-1) .. '.' .. p:sub(n+1)
  end
  local a,b
  for i=1,#s do
    a,b = s:find(withdot(i))
    if a then return a,b end
  end
end

print(myfind(a,"universe"))
person catwell    schedule 19.10.2012

Простой собственный подход (исходя из предположения, что шаблон сохраняет одинаковую длину):

function hammingdistance(a,b)
    local ta={a:byte(1,-1)}
    local tb={b:byte(1,-1)}
    local res = 0
    for k=1,#a do
        if ta[k]~=tb[k] then
            res=res+1
        end
    end
    print(a,b,res) -- debugging/demonstration print
    return res
end

function fuz(s,pat)
    local best_match=10000
    local best_location
    for k=1,#s-#pat+1 do
        local cur_diff=hammingdistance(s:sub(k,k+#pat-1),pat)
        if  cur_diff < best_match then
            best_location = k
            best_match = cur_diff
        end
    end
    local start,ending = math.max(1,best_location),math.min(best_location+#pat-1,#s)
    return start,ending,s:sub(start,ending)
end

s=[[Hello, Universe! UnIvErSe]]
print(fuz(s,'universe'))

Отказ от ответственности: не рекомендуется, просто для удовольствия:

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

getmetatable('').__sub=hammingdistance
a='Hello'
b='hello'
print(a-b)

Но обратите внимание, что a-b не равно b-a таким образом.

person jpjacobs    schedule 19.10.2012
comment
Обратите внимание, что нарушение стандартных типов обычно считается плохим стилем. - person Alexander Gladysh; 19.10.2012
comment
Но это весело! Жаль только, что нельзя сделать перезапись стандартных операторов, как сделать 1+1 == 3 ;). Но это действительно плохой стиль. Добавлен отказ от ответственности. - person jpjacobs; 19.10.2012
comment
@jpjacobs Спасибо за код. Хотя выступление станет хитом (квадратичное время?), не так ли? И я буду сравнивать довольно много слов, так что... - person SatheeshJM; 19.10.2012
comment
Все это зависит. Если он анализирует пользовательский ввод, нет проблем, пользователь будет работать медленнее. В противном случае просто попробуйте и измерьте производительность. - person jpjacobs; 19.10.2012