Algorithm 分割一串括号

Algorithm 分割一串括号,algorithm,Algorithm,有人能帮我解决这个问题吗?我将把问题打出来,然后给出我的一些想法/备选解决方案 所以问题是,给定一串括号,如下所示: [[]] 我们希望为每个括号分配一个组号(组号1或组号2)。有效赋值意味着,如果只查看组1中的方括号,它将形成一个有效、平衡的方括号字符串(类似于[][]]和不类似于]]]][]]。组2也必须如此。组不必是连续的。我们要计算将这些方括号分为两组的方法 在[[]]上面的示例字符串上,答案是6,以下是枚举:(1=组1,2=组2) 该安排不必包括所有组(如安排1和2) 思想 一个显而

有人能帮我解决这个问题吗?我将把问题打出来,然后给出我的一些想法/备选解决方案

所以问题是,给定一串括号,如下所示:

[[]]
我们希望为每个括号分配一个组号(组号1或组号2)。有效赋值意味着,如果只查看组1中的方括号,它将形成一个有效、平衡的方括号字符串(类似于[][]]和不类似于]]]][]]。组2也必须如此。组不必是连续的。我们要计算将这些方括号分为两组的方法

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

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

思想

一个显而易见的强力解决方案可以很快地处理多达32个括号,它是一个32位整数,表示哪些括号是单个组的一部分。或者我们可以使用数组。运行时是O(2^N)(我想),这太慢了

从这个问题来看,我认为给你的括号的原始字符串必须是预平衡的,否则就没有办法选择一个子集,使得组1和组2是平衡的

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

不过,我对如何将这些想法纳入算法感到困惑。我编写了暴力程序,并检查了字符串“[]”、“[[]]”、“[[[]]”和“[[[[]]”,但我并没有真正看到模式

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

"[]" = 2
"[[]]" = 6
"[[]]" = 20
"[[[[]]]]" = 70
代码:

charbuf[1000];
int N;
布尔值有效(整数掩码)
{
int-lv=0;
对于(int i=0;i如果(mask&(1如果有帮助,对于像[[]]]这样的“中心”字符串,您可以使用
ways(1)=2
ways(n)=ways(n-1)*(4*n-2)/n
(或者C(2n,n)(如果您愿意),计算您的方法数,其中n是嵌套深度

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

编辑

我们的记数能力快用完了,所以我将使用texify来表示数学公式。我想出了如下方法:

[[]]

周围的组(可以通过公式来改变)。

< P> <强>我给出了一个O(n ^ 3)-时间,O(n ^ 2)-空间动态规划解C++中的< <强> >,但是该算法的合理性首先需要说明。“子序列”是指不需要进行排序的有序子集

生成我们知道有效的字符串 将字符串的深度定义为它包含的
[
s的数量减去
]
s的数量

让我们建立一些所有有效(“平衡”)括号字符串必须遵守的规则:

  • 必须有相同数量的
    [
    s和
    ]
    s
  • 字符串的前缀不能有负深度,即大于
    [
    s的
    ]
    s
  • 这些显然是必要的条件——如果字符串违反任何一条规则,它就不可能有效。但为了能够方便地生成我们知道有效的字符串,我们需要证明这些条件也足够:任何遵守这些规则的字符串都必须是有效的。为了帮助实现这一点,让我们引入一个引理:

    引理:如果非空字符串符合条件(1)和(2),那么它必须包含
    []
    作为子字符串

    证明:它必须以
    [
    开头,否则长度1前缀将包含比
    [
    更多的
    ]
    并违反(2)。因此它必须至少包含一个
    ]
    ,否则将有i>=1
    [
    和0
    ]
    s,并且有效字符串必须包含相等数量的by(1)。因此,在某个位置j>1处必须首先出现
    ]
    ,并且其左侧的字符必须是
    [

    假设我们有一个符合条件(1)和(2)的非空字符串
    x
    。根据引理,它必须包含一个
    []
    。删除这对字符串不会导致违反这两个条件中的任何一个,因此,如果结果字符串为非空,则必须仍然遵守条件(1)和(2),因此必须仍然包含一个
    []
    某处。因此,我们可以继续删除
    []
    s,直到剩下空字符串为止

    在任何位置将
    []
    插入有效字符串都必须生成一个新的有效字符串,因为新括号对始终相互匹配,并且不会干扰任何其他匹配对。请注意,通过在空字符串中重复插入
    []
    s,可以构建原始字符串
    x
    (这是非常有效的)与我们在上一段中删除它们的顺序相反:因此我们现在证明了
    x
    (即任何符合条件(1)和(2)的字符串)是有效的

    右递归 表达OP问题的一种等效方法是:“我们可以选择多少种方法来选择字符位置的有效子序列,从而使剩余子序列也有效?”如果我们首先将其推广到:

    鉴于到目前为止,我们选择的子序列具有深度
    d
    ,而我们尚未选择的子序列具有深度
    e
    ,我们有多少种方法可以从位置
    k
    开始的后缀中选择有效的子序列,以使剩余的子序列也有效?

    调用此函数
    count(d,e,k)
    。原始问题的答案
    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;
    }
    
    #include <iostream>
    #include <vector>
    #include <string>
    
    using namespace std;
    
    class PartitionCounter {
        // Return the number of subsequences of the suffix of v beginning at position k
        // that are (a) valid, given that the initial depth of the subsequence is d (on
        // account of it being the suffix of some larger subsequence), and (b)
        // leave behind a remainder subsequence that is also valid, given that
        // the remainder sequence has initial depth depths[k]-d.
        int count(int d, int k) {
            // If a prefix of either sequence (selected or remaining) has more ']'s
            // than '['s then there can't be any completing subsequences.
            if (d < 0 || depths[k] - d < 0) {
                return 0;
            }
    
            // Only compute the answer if we haven't already.
            if (memo[d][k] == -1) {
                // A subsequence must either contain no elements, or a leftmost element
                // at some position.  All subsequences produced by recursion after this
                // initial choice are distinct (when considering the sequence of
                // character indices included, though not necessarily when considering
                // the sequence of characters themselves).
    
                // Try including no elements.  This effectively terminates the larger
                // subsequence that the selected subsequence is part of, so it can be
                // legal only if its depth is 0.  It also effectively includes all
                // remaining characters in the remainder sequence, but if the selected
                // subsequence has depth 0 and the original string does too, then it's
                // implied that the remainder must also have total depth 0, so we don't
                // need to check it.
                int n = (d == 0);
    
                // Try including a leftmost element at each remaining position.
                // If this would cause a remainder subsequence that has negative
                // depth, stop: any later loop iterations would also create illegal
                // remainder subsequences.
                for (int i = k; i < v.size() && depths[i] - d >= 0; ++i) {
                    n += count(d + v[i], i + 1);
                }
    
                memo[d][k] = n;
            }
    
            return memo[d][k];
        }
    
        vector<int> v;          // 1 for '[', -1 for ']'
        vector<int> depths;     // depths[i] is the sum of the 1st i elements
        vector<vector<int> > memo;  // DP matrix.  -1 => not computed yet
    
    public:
        PartitionCounter(string s) : memo(s.size() / 2 + 1, vector<int>(s.size() + 1, -1)) {
            depths.push_back(0);
            int total = 0;
            for (int i = 0; i < s.size(); ++i) {
                v.push_back(1 - 2 * (s[i] == ']')); // Map '[' to 1 and ']' to -1
                depths.push_back(total += v[i]);
            }
        }
    
        int count() {
            if (depths.back() == 0) {
                return count(0, 0);
            } else {
                return 0;       // Need to handle invalid strings specially
            }
        }
    };
    
    int main(int argc, char **argv) {
        PartitionCounter c(argv[1]);
        cout << c.count() << '\n';
    }
    
    C:\>partitioncounter []
    2
    
    C:\>partitioncounter [[]]
    6
    
    C:\>partitioncounter [[[]]]
    20
    
    C:\>partitioncounter [[[[]]]]
    70
    
    C:\>stopwatch partitioncounter [][[[[[][][][][[][][]]]]]][]
    10001208
    stopwatch: Terminated. Elapsed time: 15ms
    stopwatch: Process completed with exit code 0.
    
    C:\>stopwatch partitioncounter [][[[[[][[[]][][][[]]][[][]]]]]][]
    562547776
    stopwatch: Terminated. Elapsed time: 0ms
    stopwatch: Process completed with exit code 0.
    
    /*
    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;
    }
    
      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;
    }