算法用相同的字符查找最接近的字符串

时间:2009-05-13 18:00:02

标签: algorithm string permutation

给定n个字符串的列表L和输入字符串S,在L中找到包含S中存在的最多字符的字符串的有效方法是什么?我们希望在L中找到最接近S中包含的字母的字符串。

显而易见的答案是循环遍历所有n个字符串并检查当前字符串中存在多少个字符。但是,此算法将频繁运行,并且n字符串的列表L将存储在数据库中...手动遍历所有n个字符串将需要类似于n * m ^ 2的大哦,其中n是L中的字符串数,m是L中任何字符串的最大长度,以及最大值S的长度......在这种情况下,m实际上是150的常数。

有没有比简单循环更好的方法?是否有一个数据结构我可以加载n个字符串,这将给我快速搜索能力?是否有一种算法使用预先计算的关于n个字符串中每个字符串的元数据,这些字符串的性能优于循环?

我知道算法中有很多极客。所以请帮忙!

谢谢!

6 个答案:

答案 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