给定一组输入令牌(例如{a,b,c}),我正在寻找一种算法,它给出了一组尊重输入元素顺序的分区。换句话说,我正在寻找所有可能在括号周围加上括号而不改变它们的顺序。
对于{a,b,c}
,这将是(ab)c
和a(bc)
,
对于{a,b,c,d}
,它将是(ab)cd
,a(bc)d
,ab(cd)
,(abc)d
,a(bcd)
,((ab)c)d
,{{ 1}},(a(bc))d
,a((bc)d)
(我希望我得到所有这些)。
我认为这与Bell Number有某种关系,虽然它太大了,因为它也考虑像(ac)b这样的分区。
我听说有传言说这个问题可以通过推导CYK-Algorithm来解决,虽然我不明白这是怎么回事,因为CYK旨在解析CNF语法。
答案 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
所以问题是:给定输入序列,可以构造多少这样的树?这可以通过尝试根节点(a
和bcd
之间,ab
和cd
之间以及{{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;
}
它是一个高级概念伪代码,依赖于数据结构和实现,它可以解决您的问题,并具有可接受的性能。