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;
}