不同子阵列的数量

时间:2013-07-07 15:13:06

标签: arrays algorithm

我想找一个算法来计算数组中不同子数组的数量。

例如,在 A = [1,2,1,2] 的情况下, 不同子阵列的数量是7:

{ [1] , [2] , [1,2] , [2,1] , [1,2,1] , [2,1,2], [1,2,1,2]}  

并且在 B = [1,1,1] 的情况下,不同子阵列的数量为3:

{ [1] , [1,1] , [1,1,1] }

子数组是数组的连续子序列或切片。 Distinct 意味着不同的内容;例如:

来自A [0:1]的

[1]和来自A [2:3]的[1]并不明显。

同样地:

B [0:1],B [1:2],B [2:3]并不明显。

6 个答案:

答案 0 :(得分:9)

构造此数组的后缀树。然后将此树中所有边的长度加在一起。

构造后缀树所需的时间是O(n),具有适当的算法(Ukkonen's或McCreight的算法)。遍历树并将长度加在一起所需的时间也是O(n)。

答案 1 :(得分:2)

你可以简单地制作一组子序列并计算它们,但我不确定它是最有效的方法,因为它是O(n^2)

在python中的

类似于:

subs = [tuple(A[i:j]) for i in range(0, len(A)) for j in range(i + 1, len(A) + 1)]

uniqSubs = set(subs)

给你:

set([(1, 2), (1, 2, 1), (1,), (1, 2, 1, 2), (2,), (2, 1), (2, 1, 2)])

理解中的双循环清楚地说明O(n²)复杂性。

修改

显然有一些关于复杂性的讨论。由于存在O(n^2)项,因此创建子n^2

从列表中创建一个集O(m)其中m是列表的大小,m在这种情况下是n^2,因为添加到集合中是分摊的O(1)

因此整体为O(n^2)

答案 2 :(得分:1)

编辑:我考虑如何减少迭代/比较数。 我有办法做到这一点:如果你检索一个大小为n的子数组,那么每个大小都不如n的子数组都会被添加。

以下是更新的代码。

    List<Integer> A = new ArrayList<Integer>();
    A.add(1);
    A.add(2);
    A.add(1);
    A.add(2);

    System.out.println("global list to study: " + A);

    //global list
    List<List<Integer>> listOfUniqueList = new ArrayList<List<Integer>>();      

    // iterate on 1st position in list, start at 0
    for (int initialPos=0; initialPos<A.size(); initialPos++) {

        // iterate on liste size, start on full list and then decrease size
        for (int currentListSize=A.size()-initialPos; currentListSize>0; currentListSize--) {

            //initialize current list.
            List<Integer> currentList = new ArrayList<Integer>();

            // iterate on each (corresponding) int of global list
            for ( int i = 0; i<currentListSize; i++) {
                currentList.add(A.get(initialPos+i));
            }

            // insure unicity
            if (!listOfUniqueList.contains(currentList)){
                listOfUniqueList.add(currentList);                      
            } else {
                continue;
            }
        }
    }

System.out.println("list retrieved: " + listOfUniqueList);
System.out.println("size of list retrieved: " + listOfUniqueList.size());

全球研究名单:[1,2,1,2]

检索到的列表:[[1,2,1,2],[1,2,1],[1,2],[1],[2,1,2],[2,1],[ 2]]

检索到的列表大小:7

对于包含相同patern的列表,很多时候迭代次数和比较次数都会很低。 对于您的示例[1,2,1,2],行if(!listOfUniqueList.contains(currentList)){执行10次。对于包含15个不同子阵列的输入[1,2,1,2,1,2,1,2],它只增加到36。

答案 3 :(得分:0)

我的第一个答案是有点金发的时刻。

我想答案是生成所有内容然后删除重复项。或者,如果您使用带有set对象的Java之类的语言,请创建所有数组并将它们添加到一组int []中。集合只包含每个元素的一个实例,并自动删除重复项,因此您可以在结尾处获取集合的大小

答案 4 :(得分:0)

我能想到两种方式......

首先计算某种哈希然后添加到集合中。 如果添加你的哈希是相同的是一个现有的数组...然后做一个详细的比较...并记录它,以便你知道你的哈希算法不够好...

第二种是使用某种可能的匹配,然后从那里向下钻取...... 如果元素的数量相同并且加在一起的元素总数相同,那么请详细检查。

答案 5 :(得分:0)

创建一个pair数组,其中每个对存储子数组元素及其索引的值。

pair[i] = (A[i],i);

A[i]的递增顺序对对进行排序,然后按i的降序排序。

考虑示例A = [1,3,6,3,6,3,1,3];
排序后的对数组将为pair = [(1,6),(1,0),(3,7),(3,5),(3,3),(3,1),(6,4),(6,2)]

pair[0]的元素为index 6。从index 6我们可以有两个子阵列[1][1,3]。所以ANS = 2;
现在逐一取每一对。
pair[0]pair[1]
pair[1]的索引为0.我们可以从index 0开始有8个子数组。但是已经计算了两个子阵列[1]和[1,3]。因此,要删除它们,我们需要比较pair[0]pair[1]的子数组的最长公共前缀。因此,从0和6开始的索引的最长公共前缀长度是2,即[1,3] 所以现在新的不同子阵列将是[1,3,6] ..到[1,3,6,3,6,3,1,3],即6个子阵列。 所以ANS的新值是2 + 6 = 8;

因此pair[i]pair[i+1]ANS = ANS + Number of sub-arrays beginning from pair[i+1] - Length of longest common prefix

排序部分需要O(n logn) 迭代每个连续对是O(n),并且对于每次迭代,找到最长公共前缀取O(n)使得整个迭代部分O(n ^ 2)。这是我能得到的最好的。

你可以看到我们不需要配对。对的第一个值,元素的值不是必需的。我用它来更好地理解。你总是可以跳过它。