从列表列表

时间:2017-09-05 17:03:26

标签: string algorithm

我有几个对象,每个对象都包含一个字符串列表。

Object 1
["one", "two", "three", "four"]
Object 2
["one", "two", "five", "seven", "eight"]
Object 3
["one", "two", "three", "four", "five", "seven"]
Object 4
["one", "two", "nine", "ten"]

正如您所看到的,字符串是重复的,我的任务是对这些字符串进行分组,并在对象中引用这些组而不是字符串本身。输出应如下所示:

group1
["one", "two"]
group2
["three", "four"]
group3
["five", "seven"]
group4
["eight"]
group5
["nine", "ten"]

该对象将包含对组的引用

Object 1
["group1", "group2"]
Object 2
["group1", "group3", "group4"]
Object 3
["group1", "group2", "group3"]
Object 4
["group1", "group5"]

值得庆幸的是,这个分组是在一组静态数据上进行的,每个对象由大约20个这样的字符串组成。

如果一个字符串出现在两个组中并不重要,但这是一件好事,因此,除非创建这样的组非常容易,否则此时不是一个目标。

我使用字符串作为示例简化了我的问题,但在我的文档中,它们是反复重复的JSON对象。目标是对它们进行分组并改为引用这些组,以便不会一次又一次地重复相同的对象。所以,说,目标是减少重复。

我知道这是某种clustering problem,但我无法将这些字符串形式化为(x,y)平面中的点。 我该如何处理?这个问题有名字吗?

编辑:经过一番思考后,我想我应该在所有这些列表中找到不相交的字符串集并从那里开始工作。联合查找算法似乎很合适。

3 个答案:

答案 0 :(得分:1)

我在$JOB的某个程序中执行了非常类似的计算,只要我保持通用,就可以安全地分享。

我将在python3中编写这个,我正在用一种非常不同的语言进行翻译,所以可能会出现漏洞,肯定有一些地方你可以找到明显的速度提升:

def findGroups(llStrs):
  # Input should be a list of lists of strings
  all_things = frozenset.union(frozenset(), *(map(frozenset, llStrs)))
  all_singletons = frozenset.union(frozenset(), *(frozenset(x) for x in llStrs if len(x) == 1))
  working = [all_things - all_singletons]
  for lStr in llStrs:
    setLStr = frozenset(lStr)
    working2 = []
    for w in working:
      if (w & setLStr) and not (w <= setLStr):
        a = w & setLStr
        b = w - setLStr
        if len(a) > 1:
          working2.append(a)
        if len(b) > 1:
          working2.append(b)
      else:
        working2.append(w)
    working = working2
  final_working_union = frozenset.union(*working)
  return working + [frozenset({x}) for x in (all_things - final_working_union)]

现在,这可能不是你想要的 - 每个组都是不相交的,而且如果有时组重叠,那么对你的使用来说可能会更好。但是,这大致就是我在项目中分组的方式。

答案 1 :(得分:1)

以下是Python中的一个示例。我们定义一个组类来携带一个列表和一个别名。然后我们遍历我们想要压缩的Object列表,直到不再可能进行替换。请注意,我对组列表进行排序,以使较长的匹配优先于较短的匹配。

union-find算法在这里不会帮助你,因为它用于查看两个节点在将图形(父子映射)作为输入时是否在同一组中,然后合并两个集合(如果它们是不。您可以找到更多时间有效的方法来判断两个元素是否是同一组的成员(例如,由单个元素键入的字典以及指向其所在组的指针,基本上是扁平的不相交的集合看起来像)但我不知道这将如何帮助您解决您描述的问题。您没有正确的输入,也不需要所提到的算法的输出,所以我不知道在这种情况下它对您有什么用处。

您所描述的是匹配有序子集 [c n ,..,c n + m ]某些集合[c 0 ,..,c j ]完全等于已定义的组[f 0 ,..,f m ](压缩不会如果我们最多可以将两个元素分组,那就非常有效,这就是为什么我们将组长度推广到m)。在我看来,使用不相交集结构并没有真正的好处。

最后,如果您的目标是通过选择频繁重复的子序列的最佳替换组来最小化您的内存占用,我建议您查看数据压缩主题。

class group:
    def __init__(self,stringList, name):
        self.list = stringList
        self.alias = name

class object1:
    def __init__(self,stringList):
        self.list = stringList

g1 = group(["one", "two"], "group1")
g2 = group(["five", "seven"], "group2")
g3 = group(["seven"], "group3")
g4 = group(["three", "five", "seven"], "group4")
o = object1(["one", "two", "three", "five", "seven", "seven", "eight", "nine"])
group_list = [g1, g2, g3, g4]
group_list.sort(key=lambda x: len(x.list), reverse=True)

def match_elems(match_group, sub_list, index):
    for i in range(index, min(index + len(match_group.list), len(sub_list))):
        if match_group.list[i-index] != sub_list[i]:
            return False
    return True

def compress(obj, groups):
    for g in group_list:
        n = 0
        while n < len(obj.list):
            if match_elems(g, obj.list, n):
                obj.list = obj.list[:n] + [g.alias] + obj.list[n:]
                obj.list = obj.list[:n+1] + obj.list[n+len(g.list)+1:]
                print(obj.list)
            n=n+1

compress(o, group_list)

答案 2 :(得分:1)

p = ["one", "two", "three", "four"]

q = ["one", "two", "five", "seven", "eight"]

r = ["one", "two", "three", "four", "five", "seven"]

s = ["one", "two", "nine", "ten"]

p, q, r, s = map(set, [p, q, r, s])
u = p | q | r | s
groups = {}
elements = [p, q, r, s]
index_map = {i:set() for i, _ in enumerate(elements)}
for i in u:
    count = 0
    current = []
    for k, j in enumerate(elements):
        if {i}.issubset(j):
            count += hash(count + 1 + k)
            current += [k]
    if not count in groups:
        groups[count] = [i]
    else:
        groups[count] += [i]
    for t in current:
        index_map[t] |= {count}
print groups
print index_map

上面代码背后的想法是检查元素在不同集合中重复的次数,基于该元素在字典中赋予该元素唯一键。因此,所有集合中的每个元素都将归入相同的键或元素,在N个集合中重复将具有唯一键。一旦我们拥有唯一的密钥,我们就必须将原始数据与新数据进行映射,为此,我要跟踪在不同集合中具有当前元素的元素索引。最后我执行了set union。