分区一串括号

时间:2012-11-18 02:45:31

标签: algorithm

有人可以帮我解决这个问题吗?我会输入问题,然后给出一些我的想法/替代解决方案。

所以问题就在于,给出一个这样的括号:

[[]]

我们希望为每个括号分配一个组号(组1或组2)。一个有效的赋值意味着如果你只看第一组中的括号,它会形成一个有效的,平衡的括号字符串(这就像[] [[]]之类的东西,而不是像[] [] []那样的东西。同样的第二组是正确的。组不必是连续的。我们想要计算将这些括号分成两组的方法。

在[[]]上面的示例字符串上,答案是6,这里是枚举:(1 =组1,2 =组2)

  [[]]
1.1111
2.2222
3.1221
4.2112
5.1212
6.2121

该安排不必包括所有组(如安排1.和2)。

思想

一个明显的暴力解决方案,最多可以使用32个括号,相当快速,是一个32位整数,表示哪个括号是单个组的一部分。或者我们可以使用数组。运行时间是O(2 ^ N)(我想),这太慢了?

通过查看问题,我认为您给出的原始括号字符串必须预先平衡,否则无法选择子集,使第1组和第2组达到平衡。

我还注意到你可以分开组件 - 字符串“[]”有2个排列,所以字符串“[] []”有4个排列。 (您可以在每个组件中找到方法的数量并将它们相乘)。

我对如何将这些想法纳入算法感到困惑。我写了蛮力程序,我检查了字符串“[]”,“[[]]”,“[[[]]]”和“[[[[]]]]”,我不是真的看一个模式。

从将这些字符串插入我的暴力程序后,我得到:

"[]" = 2
"[[]]" = 6
"[[]]" = 20
"[[[[]]]]" = 70

代码:

char buf[1000];
int N;
bool isValid(int mask)
{
    int lv = 0;
    for (int i = 0; i < N; i++)
    {
        if (mask & (1 << i))
        {
            if (buf[i] == '(')
            {
                lv++;
            }
            else
            {
                lv--;
            }
            if (lv<0)
            {
                return false;
            }
        }
    }
    return lv==0;
}

int main() 
{
    scanf("%s", buf);
    N = strlen(buf);
    int ways = 0;
    for (int i = 0; i < (1 << N); i++)
    {
        if (isValid(i) && isValid(~i))
        {
            ways++;
        }
    }
    printf("Number of ways is %d\n", ways);
    return 0;
}

3 个答案:

答案 0 :(得分:2)

如果有任何帮助,对于[[[[]]等“中心”字符串,您可以使用ways(1) = 2ways(n) = ways(n-1)*(4*n-2)/n(或 C(2n,n)来计算您的使用方式)如果你愿意的话),其中n是嵌套的深度。

嵌套但不是“中心”的组(如[[] []])似乎遵循类似的模式,但我无法找出正确的公式。

修改

我们的符号功率耗尽,所以我将使用texify来表达数学公式。我想出了类似的东西:

formula

周围的群组(您可以按this更改公式)。

答案 1 :(得分:2)

扩展ishi的答案,我认为可以在O(N ^ 2)中完成,因为d + e等于前缀的深度。下面的代码得到了相同的结果。

/*
How many ways are there to split a string
of brackets into two such that both are balanced.
*/

#include <iostream>
#include <string>
using namespace std;

#define MAXN 1000
#define M 1000000007

int N, dp[MAXN][MAXN];
string s;

int recurse(int k, int i, int p) {
  if (i >= N) {
    return k == 0 ? 1 : 0;
  }
  if (k < 0 || p-k < 0) {
    return 0;
  }

  int &ans = dp[k][i];
  if (ans != -1) {
    return ans;
  }

  ans = 0;
  if (s[i] == '[') {
    ans += recurse(k+1,i+1,p+1)+recurse(k,i+1,p+1);
    return ans;
  }
  if (s[i] == ']') {
    ans += recurse(k-1,i+1,p-1)+recurse(k,i+1,p-1);
    return ans;
  }
  return 0;
}

int main() {
  cin >> s;
  N = s.size();
  for (int k = 0; k < N; k++) {
    for (int i = 0; i < N; i++) {
      dp[k][i] = -1;
    }
  }
  cout << recurse(0,0,0) << endl;
}

答案 2 :(得分:1)

更容易理解的解决方案。

  public int splitString(String s){
    int gOneC=0;
    int gTwoC=0;

    Map<String, Integer> memo = new HashMap<>();
    return splitStringRecur(s,0, gOneC, gTwoC, memo);
}

private int splitStringRecur(String s, int i, int gOneC, int gTwoC, Map<String, Integer> memo) {
    if(i == s.length()){
        if(gOneC==0 || gTwoC==0) return 1;
    }
    String t = i+"-"+gOneC+"-"+gTwoC+"";
    if(memo.containsKey(t)) return memo.get(t);

    int gc =0;
    int gs =0;
    if(s.charAt(i)=='('){
        gc = splitStringRecur(s, i+1, gOneC+1, gTwoC,memo);
        gs = splitStringRecur(s, i+1, gOneC, gTwoC+1,memo);
    }else {
        if(gOneC > 0){
          gc +=  splitStringRecur(s, i+1, gOneC-1, gTwoC,memo);
        }
        if( gTwoC >0){
           gs += splitStringRecur(s, i+1, gOneC, gTwoC-1,memo);
        }
    }
    memo.put(t, gc+gs);
    return gc+gs;
}