有序分区集的算法

时间:2016-06-02 00:46:47

标签: algorithm math

给定一组输入令牌(例如{a,b,c}),我正在寻找一种算法,它给出了一组尊重输入元素顺序的分区。换句话说,我正在寻找所有可能在括号周围加上括号而不改变它们的顺序。

对于{a,b,c},这将是(ab)ca(bc)

对于{a,b,c,d},它将是(ab)cda(bc)dab(cd)(abc)da(bcd)((ab)c)d,{{ 1}},(a(bc))da((bc)d)(我希望我得到所有这些)。

我认为这与Bell Number有某种关系,虽然它太大了,因为它也考虑像(ac)b这样的分区。

我听说有传言说这个问题可以通过推导CYK-Algorithm来解决,虽然我不明白这是怎么回事,因为CYK旨在解析CNF语法。

3 个答案:

答案 0 :(得分:1)

假设您只在顶层进行分区,这意味着对于集<div id="main_navigation" class="column"> <ul> {{#navigation}} <div class="dropdown"> <button class="{{#is_current}}selected{{/is_current}} dropbtn"> <a href="{{url}}"> {{label}} </a> </button> {{#is_current}} {{! Render the submenu if it has any navigation items }} {{#children?}} <div class="child_navigation dropdown-content"> {{#children}} <a href="{{url}}"> {{label}} </a> {{/children}} </div> {{/children?}} {{/is_current}} </div> {{/navigation}} 您有以下分区:

{a,b,c,d}

你可以用两个for循环生成这些,外循环用于括号内的项目数(从2到集合中的项目数减去1),内循环用于之前的项目数量开始括号(从0到集合中的项目数减去括号内的数字)。所以在上面的例子中,外部循环从2到3(包括2和3)迭代,内部循环从0到2迭代,第一次通过,0到1,第二次。

完成此操作后,只需递归地对括号内的项进行分区即可获得完整的分区集。一个棘手的部分是,在顶级,你没有(显然)想要输出没有括号作为有效分区的所有项目,但当你递归时,你做了。

以下是Java中使用字符串执行此操作的一些简单代码:

(ab)cd
a(bc)d
ab(cd)
(abc)d
a(bcd)

输出:

public static void outputPartitions(String head, String partition, String tail, boolean top)
{
    int len = partition.length();
    if(!top) // only output the items without brackets when not at the top level
        System.out.println(head + partition + tail);

    for(int i = 2; i <= len-1; i++)
    {
        for(int j = 0; j <= len-i; j++)
        {
            outputPartitions(head + partition.substring(0, j)+"(",
               partition.substring(j, j+i),
               ")"+partition.substring(j+i)+tail, false);
        }
    }
}

public static void main (String[] args) throws java.lang.Exception
{
    outputPartitions("", "abcd", "", true);
}

答案 1 :(得分:1)

每个完整的括号(每个括号对只包含两个元素)可以被认为是树结构,其中每个括号对是一个节点,它包含的两个元素是它的子节点。例如(a((bc)d)),是:

  ()
 /  \
a   ()
   /  \
  ()   d
 /  \
b    c

所以问题是:给定输入序列,可以构造多少这样的树?这可以通过尝试根节点(abcd之间,abcd之间以及{{1}之间)的所有可能“分割点”来解答}和abc),并递归地尝试子序列的分裂点。

请注意,这与matrix-chain multiplication密切相关。

答案 2 :(得分:1)

这个问题很有意思,我很期待其他解决方案用一些已知的有前途的算法来解决这个问题......同时我会提出我的想法。

我不知道你的有序集是什么数据结构 如果我不理解你的问题,我会有一个简单的想法:

也许我们可以使用递归公式的简单递归,如下所示

Split(ordered set A){
    if(len(A) == 1) print(A[0])
    string group = "";
    for(int i in [1, len(A)]){
        group = group+" "+A[i];
        A remove A[i];
        print(group + "," + Split(A));    
    }
}

基本上它是循环集合,从第一个元素到最后一个元素,在每次迭代时,将第一个i元素作为第一个分区,然后删除这些i元素,再次调用相同的函数(递归) )(取决于您的数据结构,您可能不需要物理删除但只传递集合A的一部分,无论如何它是概念)

这种方式的表现很慢,但我认为考虑到你必须得到所有组合,这可能是可以接受的。

你可以通过一些改动加快速度:实际上我们只需要知道集合的大小来获得分区,前4个元素,或中间4个连续元素,或集合中任何地方的4个连续元素,它们所有都可以用相同的方式进行分区。所以我们确实只需要保存所有使用n元素对集合进行分区的方法,这可以增强上面的动态编程递归:

vector<vector<int>> pos[n]; 
// pos[i] is my way to store the "configuration" of the partition for i elements
// for example:  pos[7] may store [[1,3,7], [2,4,7]]
// Means (1..1),(2..3),(4..7) is one way to partition 7 elements
// (1..2),(3..4),(5..7) is another way
// I want to find pos with this dynamic programming method

Split(int size){
    if(pos[size] is not empty){
       return pos[size];
    }
    if(size == 1){
       push [1] into pos;
       return pos;
    }
    vector<vector<int>> ret;
    for(int i in [1..n]){
        vector<vector<int>> subPartitions= Split(n-i);
        for(vector<int> partition in [1..size(subPartitions)]){
             Add i (offset) to all element in partition
             vector<int> newPartition = Merge([i], parition);
             push newPartition to ret;
        }
    }
    return pos[size] = ret;
}

它是一个高级概念伪代码,依赖于数据结构和实现,它可以解决您的问题,并具有可接受的性能。