最常见的组合

时间:2013-03-03 16:23:28

标签: algorithm set

我有一个整数数组列表,其中每个数组都有一些数字排序。在这里,我想找到基于所有数组的最常出现的整数序列组合。例如,如果数组列表如下

A1 - 1 2 3 5 7 8
A2 - 2 3 5 6 7
A3 - 3 5 7 9
A4 - 1 2 3 7 9
A5 - 3 5 7 10

下面

{3,5,7} - {A1,A3,A5}
{2,3}   - {A1,A2,A4}

因此我们可以将{3,5,7}或{2,3}作为最常见的组合。

现在我使用的算法如下

查找集合与所有其他集合的交集。并存储结果集。如果已存在,则增加结果集事件。 例如: 找到以下所有的交叉点

A1 intersection A2 
A1 intersection A3
A1 intersection A4
A1 intersection A5
A2 intersection A3  
A2 intersection A4 
A2 intersection A5  
A3 intersection A4
A3 intersection A5  
A4 intersection A5  

这里A1交点A3与A3交点A5相同,因此set- {3,5,7}出现可以设为2。 类似地,可以确定每个结果集的出现次数。

但是这种算法要求O(n ^ 2)复杂度。 假设每个集合都已排序,我很确定我们可以找到一个更好的算法,其O(n)复杂度是我无法记下的。 任何人都可以建议相同的O(n)算法。

2 个答案:

答案 0 :(得分:1)

如果你有一个长度为n的序列,那么它的前缀长度为n-1,并且至少经常发生 - 退化情况是最常见的字符,它是一个长度为1的序列,至少经常出现任何更长的序列。您是否有您感兴趣的最小后缀长度?

无论如何,一个想法是连接所有序列,用不同的整数分隔它们,然后在线性时间内计算http://en.wikipedia.org/wiki/Suffix_array。一次通过后缀数组应该允许你找到任何给定长度的最常见的子序列 - 并且它不应该跨越两个不同数组之间的间隙,因为每个这样的长度为n的序列是唯一的,因为字符分隔了数组是唯一的。 (另见http://en.wikipedia.org/wiki/LCP_array

答案 1 :(得分:0)

Haskell中的此示例不扫描交叉点。相反,它列出每个列表的子序列并将它们聚合成由子序列索引的数组。要查找最常出现的子序列,只需显示数组中最长的元素。输出被过滤以显示大于长度1的子序列。输出是元组列表,显示子序列和子序列出现的列表的索引:

*主> combFreq [[1,2,3,5,7,8],[2,3,5,6,7],[3,5,7,9],[1,2,3,7,9], [3,5,7,10]]
[([3,5],[4,2,1,0]),([5,7],[4,2,0]),([3,5,7],[4,2,0 ]),([2,3],[3,1,0]),([7,9],[3,2]),([2,3,5],[1,0]),( [1,2,3],[3,0]),([1,2],[3,0])]

import Data.List
import qualified Data.Map as M
import Data.Function (on)

sInt xs = concat $ zipWith (\x y -> zip (subs x) (repeat y)) xs [0..]
  where subs = filter (not . null) . concatMap inits . tails

res xs = foldl' (\x y -> M.insertWith (++) (fst y) [snd y] x) M.empty (sInt xs)

combFreq xs = reverse $ sortBy (compare `on` (length . snd)) 
            . filter (not . null . drop 1 . snd)
            . filter (not . null . drop 1 . fst)
            . M.toList
            . res $ xs