给定n个字符串的列表L和输入字符串S,在L中找到包含S中存在的最多字符的字符串的有效方法是什么?我们希望在L中找到最接近S中包含的字母的字符串。
显而易见的答案是循环遍历所有n个字符串并检查当前字符串中存在多少个字符。但是,此算法将频繁运行,并且n字符串的列表L将存储在数据库中...手动遍历所有n个字符串将需要类似于n * m ^ 2的大哦,其中n是L中的字符串数,m是L中任何字符串的最大长度,以及最大值S的长度......在这种情况下,m实际上是150的常数。
有没有比简单循环更好的方法?是否有一个数据结构我可以加载n个字符串,这将给我快速搜索能力?是否有一种算法使用预先计算的关于n个字符串中每个字符串的元数据,这些字符串的性能优于循环?
我知道算法中有很多极客。所以请帮忙!
谢谢!
答案 0 :(得分:6)
如果你在追踪子串,Trie或Patrica trie可能是一个很好的起点。
如果您不关心顺序,只关心每个符号或字母的数量,我会计算所有字符串的直方图,然后将它们与输入的直方图进行比较。
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Hello World => ...11..1...3..2..1....1...
如果您只考虑不区分大小写的拉丁字母,这会将成本降低到O(26 * m + n)
加上预处理一次。
如果m是常数,则可以通过对直方图进行归一化将直方图解释为26维单位球面上的26维向量。然后你可以计算两个向量的Dot Product,得到两个向量之间角度的余弦,这个值应该与字符串的相似性成正比。
假设m = 3
,字母A = { 'U', 'V', 'W' }
只有三个字符串,以及以下字符串列表。
L = { "UUU", "UVW", "WUU" }
直方图如下。
H = { (3, 0, 0), (1, 1, 1), (2, 0, 1) }
直方图h = (x, y, z)
被归一化为h' = (x/r, y/r, z/r)
,r
直方图h
的欧几里德范数 - 即r = sqrt(x² + y² + z²)
。
H' = { (1.000, 0.000, 0.000), (0.577, 0.577, 0.577), (0.894, 0.000, 0.447) }
输入S = "VVW"
包含直方图hs = (0, 2, 1)
和标准化直方图hs' = (0.000, 0.894, 0.447)
。
现在我们可以计算两个直方图h1 = (a, b, c)
和h2 = (x, y, z)
的相似度作为两个直方图的欧几里德距离。
d(h1, h2) = sqrt((a - x)² + (b - y)² + (c - z)²)
我们获得的例子。
d((3, 0, 0), (0, 2, 1)) = 3.742
d((1, 1, 1), (0, 2, 1)) = 1.414
d((2, 0, 1), (0, 2, 1)) = 2.828
因此“UVW”最接近“VVW”(较小的数字表示较高的相似性)。
使用标准化直方图h1' = (a', b', c')
和h2' = (x', y', z')
,我们可以将距离计算为两个直方图的点积。
d'(h1', h2') = a'x' + b'y' + c'z'
我们获得的例子。
d'((1.000, 0.000, 0.000), (0.000, 0.894, 0.447)) = 0.000
d'((0.577, 0.577, 0.577), (0.000, 0.894, 0.447)) = 0.774
d'((0.894, 0.000, 0.447), (0.000, 0.894, 0.447)) = 0.200
再次确定“UVW”最接近“VVW”(较大的数字表示较高的相似度)。
两个版本都会产生不同的数字,但结果总是相同的。人们也可以使用其他规范 - 例如曼哈顿距离(L1范数) - 但这只会改变数字,因为有限维向量空间中的范数都是等价的。
答案 1 :(得分:1)
听起来你需要trie。尝试用于搜索与拼写检查器的工作方式类似的单词。因此,如果字符串S的字符与L中的字符串的顺序相同,那么这可能适合您。
但是,如果S中字符的顺序不相关 - 就像一组拼字游戏拼贴而你想搜索最长的单词 - 那么这不是你的解决方案。
答案 2 :(得分:1)
你想要的是BK-Tree。它有点不直观,但非常酷 - 它可以在O(log n)时间内在levenshtein(编辑)距离阈值内搜索元素。
如果您关心输入字符串中的顺序,请按原样使用它们。如果不这样做,您可以在将各个字符插入BK树之前对其进行排序(或使用它们进行查询)。
答案 3 :(得分:0)
我相信您所寻找的内容可以在这里找到:Fuzzy Logic Based Search Technique
这很重,但你所要求的也是如此。它谈论的是相似词和人格错位。
i.e:
L I N E A R T R N A S F O R M
L I N A E R T R A N S F O R M
L E N E A R T R A N S F R M
答案 4 :(得分:0)
在我看来,角色的顺序在你的问题中并不重要,但是你正在搜索单词S的“near-anagrams”。
如果是这样,那么你可以将集合L中的每个单词表示为26个整数的数组(假设你的字母表有26个字母)。您可以将S表示为26个整数的数组;现在找到你刚刚通过集合L运行一次的最佳匹配并计算S向量和当前L向量之间的距离度量,但是你想要定义距离度量(例如欧几里德/平方和或曼哈顿) /绝对差异之和)。这是O(n)算法,因为向量具有恒定的长度。
答案 5 :(得分:0)
这是一个T-SQL函数,它一直很适合我,给你编辑距离:
示例:
SELECT TOP 1 [StringValue] , edit_distance([StringValue, 'Input Value')
FROM [SomeTable]
ORDER BY edit_distance([StringValue, 'Input Value')
功能:
CREATE FUNCTION edit_distance(@s1 nvarchar(3999), @s2 nvarchar(3999))
RETURNS int
AS
BEGIN
DECLARE @s1_len int, @s2_len int, @i int, @j int, @s1_char nchar, @c int, @c_temp int,
@cv0 varbinary(8000), @cv1 varbinary(8000)
SELECT @s1_len = LEN(@s1), @s2_len = LEN(@s2), @cv1 = 0x0000, @j = 1, @i = 1, @c = 0
WHILE @j <= @s2_len
SELECT @cv1 = @cv1 + CAST(@j AS binary(2)), @j = @j + 1
WHILE @i <= @s1_len
BEGIN
SELECT @s1_char = SUBSTRING(@s1, @i, 1), @c = @i, @cv0 = CAST(@i AS binary(2)), @j = 1
WHILE @j <= @s2_len
BEGIN
SET @c = @c + 1
SET @c_temp = CAST(SUBSTRING(@cv1, @j+@j-1, 2) AS int) +
CASE WHEN @s1_char = SUBSTRING(@s2, @j, 1) THEN 0 ELSE 1 END
IF @c > @c_temp SET @c = @c_temp
SET @c_temp = CAST(SUBSTRING(@cv1, @j+@j+1, 2) AS int)+1
IF @c > @c_temp SET @c = @c_temp
SELECT @cv0 = @cv0 + CAST(@c AS binary(2)), @j = @j + 1
END
SELECT @cv1 = @cv0, @i = @i + 1
END
RETURN @c
END