Algorithm 计算置换中有效块数的算法

Algorithm 计算置换中有效块数的算法,algorithm,permutation,Algorithm,Permutation,可能重复: 给定一个数组A,它的排列为1,2,…,n。子块A[i..j] 如果[i..j]中出现的所有数字都被称为有效块 是连续的数字(可能不按顺序排列) 给定一个数组A=[7 3 4 1 2 6 5 8],有效块为[3 4],[1,2],[6,5], [3 4 1 2],[3 4 1 2 6 5],[7 3 4 1 2 6 5],[7 3 4 1 2 6 8] 所以上述排列的计数是7 给出一个O(n logn)算法来计算有效块的数量。My proposition 步骤=2//检查编号的数量

可能重复:

给定一个数组A,它的排列为1,2,…,n。子块A[i..j]
如果[i..j]中出现的所有数字都被称为有效块 是连续的数字(可能不按顺序排列)

给定一个数组A=[7 3 4 1 2 6 5 8],有效块为[3 4],[1,2],[6,5],
[3 4 1 2],[3 4 1 2 6 5],[7 3 4 1 2 6 5],[7 3 4 1 2 6 8]

所以上述排列的计数是7

给出一个O(n logn)算法来计算有效块的数量。

My proposition

步骤=2//检查编号的数量

B[0,0,0,0,0,0,0,0]

B[1,1,0,0,0,0,0]

有效(A,B)-如果无效,则移动一个

B[0,1,1,0,0,0,0,0]

有效(A,B)-如果有效,移动一步

B[0,0,0,1,1,0,0,0]

有效(A、B)

B[0,0,0,0,0,1,1,0]

步骤=3

B[1,1,1,0,0,0,0]不正常

B[0,1,1,1,0,0,0,0]正常

B[0,0,0,0,1,1,1,0]不正常

步骤=4

B[1,1,1,1,0,0,0]不正常

B[0,1,1,1,1,0,0,0]正常


CON好的,我只剩1个代表,因为我在一个相关问题上悬赏了200美元:
所以我暂时不能留下评论

我有一个想法: 1) 找到所有置换组。它们是:(78)、(34)、(12)、(65)。与群论不同,它们的顺序和位置,以及它们是否相邻。因此,组(78)可以表示为结构
(7,8,false)
,而(34)可以表示为
(3,4,true)
。我对元组使用Python的表示法,但实际上,对组使用整个类可能更好。这里“真”或“假”表示连续或不连续。如果
(max(gp1)=min(gp2)+1或max(gp2)=min(gp1)+1)和contigous(gp1)和contiguos(gp2)
,则两组是“相邻的”。这不是唯一的条件,因为
(14)
(23)
很好地结合成
(14)
。这对于algo课堂作业来说是一个很好的问题,但对于面试来说却是一个糟糕的问题。我怀疑这是家庭作业。

只是一些想法:

乍一看,这听起来是不可能的:一个完全排序的数组将有O(n2)个有效子块

因此,一次需要计算多个有效子块。检查子块的有效性为O(n)。检查子块是否已完全排序也是O(n)。一个完全排序的子块包含n·(n-1)/2个有效的子块,您可以在不进一步分解该子块的情况下对其进行计数

现在,整个数组显然总是有效的。对于分而治之的方法,您需要将其分解。有两个可能的断裂点:最高元素的位置和最低元素的位置。如果在其中一个点(包括包含第二个至极值元素的零件中的极值)将阵列拆分为两个,则不能有有效的子块穿过此断点

通过总是选择产生更均匀分裂的极值,这对于“随机”数组应该非常有效(平均O(n logn))。然而,当您的输入类似于
(1 5 2 6 3 7 4 8)
时,我可以看到问题,这似乎会产生O(n2)行为<代码>(1 4 7 2 5 8 3 6 9)
也会类似(我希望您能看到模式)。我目前看不到抓住这种更糟糕情况的诀窍,但它似乎需要其他拆分技术。

(这是一种尝试,尝试这样做N.log(N)最坏的情况。不幸的是,这是错误的——它有时计数不足。它错误地假设您可以通过只查看相邻的一对较小的有效块来找到所有的块。事实上,您必须查看三元组、四元组等,才能获得所有较大的块。)

您可以使用表示子块和子块队列的结构来执行此操作

  struct
c_subblock
{
  int           index   ;  /* index into original array, head of subblock */
  int           width   ;  /* width of subblock > 0 */
  int           lo_value;
  c_subblock *  p_above ;  /* null or subblock above with same index */
};
分配一个与原始数组大小相同的子块数组,并初始化每个子块,使其仅包含一项。在运行时将它们添加到队列中。如果从数组[7 3 4 1 2 6 5 8]开始,您将得到如下队列:

排队:([7,7][3,3][4,4][1,1][2,2][6,6][5,5][8,8])

子块[7,7]的{index,width,lo_值,p_over}值将是{0,1,7,null}

现在很容易了。请原谅c-ish伪代码

loop {
  c_subblock * const p_left      = Pop subblock from queue.
  int          const right_index = p_left.index + p_left.width;
  if ( right_index < length original array ) {
    // Find adjacent subblock on the right.
    // To do this you'll need the original array of length-1 subblocks.
    c_subblock const * p_right = array_basic_subblocks[ right_index ];
    do {
      Check the left/right subblocks to see if the two merged are also a subblock.
        If they are add a new merged subblock to the end of the queue.
      p_right = p_right.p_above;
    }
    while ( p_right );
  }
}
循环{
c_子块*const p_left=从队列中弹出子块。
int const right_index=p_left.index+p_left.width;
if(右索引<原始数组长度){
//在右边找到相邻的子块。
//要做到这一点,您需要原始的长度为1的子块数组。
c_子块常量*p_right=数组_基本_子块[右索引];
做{
检查左/右子块,查看合并的两个子块是否也是子块。
如果是,则在队列末尾添加新的合并子块。
p_right=p_right.p_以上;
}
而(p_右),;
}
}
我想这些都会找到的。它通常是O(N log(N)),但对于完全排序或反排序的列表,它将是O(N^2)。我认为这有一个答案——当你构建原始的子块数组时,你会寻找排序和反排序的序列,并将它们添加为基本级别的子块。如果保持计数,则将其增加(宽度*(宽度+1))/2作为基准标高。这将为您提供包括所有1长度子块在内的计数

  struct
c_subblock
{
  int           index   ;  /* index into original array, head of subblock */
  int           width   ;  /* width of subblock > 0 */
  int           lo_value;
  c_subblock *  p_above ;  /* null or subblock above with same index */
};
之后,只需使用上面的循环,弹出并推送队列。如果你在计算,你必须在左右两个子块上都有一个乘法器,然后把它们相乘,计算出增量。乘法器是最左侧(对于p_left)或最右侧(对于p_right)基本层级子块的宽度

希望这是明确的,不要太多车。我只是在敲打它,所以它甚至可能是错误的。
[稍后注意。这根本不起作用。请参见下面的注意。]

和其他人一样,我只是把它扔掉了。。。它适用于下面的单个示例,但是