我想找一个算法来计算数组中不同子数组的数量。
例如,在 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]并不明显。
答案 0 :(得分:9)
构造此数组的后缀树。然后将此树中所有边的长度加在一起。
构造后缀树所需的时间是O(n),具有适当的算法(Ukkonen's或McCreight的算法)。遍历树并将长度加在一起所需的时间也是O(n)。
答案 1 :(得分:2)
你可以简单地制作一组子序列并计算它们,但我不确定它是最有效的方法,因为它是O(n^2)
。
类似于:
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)。这是我能得到的最好的。
你可以看到我们不需要配对。对的第一个值,元素的值不是必需的。我用它来更好地理解。你总是可以跳过它。