问题描述
Educational Codeforces Round 105 的问题 A(评分为第 2 部分)。 Um_nik 用这种蛮力方法解决了这个问题,我并不完全理解。 我知道什么是位掩码,但我对 if ((mask >> (int)(s[i] - 'A')) & 1) 和 ok &= bal >= 0 感到困惑。 问题的链接是:https://codeforces.com/contest/1494/problem/A
cin >> s;
int n = (int)s.length();
//cout << s << endl;
for (int mask = 0; mask < (1 << 3); mask++) {
int bal = 0;
bool ok = true;
for (int i = 0; i < n; i++) {
if ((mask >> (int)(s[i] - 'A')) & 1)
baL++;
else
bal--;
ok &= bal >= 0;
}
ok &= bal == 0;
if (ok) {
printf("YES\n");
return;
}
}
printf("NO\n");
解决方法
我知道问题所在!
m
的范围为0b000 ~ 0b111
,第i位表示如果'A' + i
是符号(
(如果不是,则是符号{{ 1}}).
例如,)
表示 m = 0b010
。我需要使用 {'A': ')','B': '(','C': ')'}
来表示 m[CHAR]
,所以 (m >> (CHAR - 'A') & 1) ? '(' : ')'
。
我们知道m['B'] == '('
是字符串s[i]
中的第i个字符,所以s
的意思是m >> (s[i] - 'A') & 1
,如果第i个字符表示符号 m[s[i]]
。
例如(
、s = "ABBC"
和m = 0b010
,所以i = 2
,你就会知道s[i] == 'B'
最后,我们需要在开始时设置状态为m >> ('B' - 'A') & 1 == m['B'] == '('
,然后如果满足0
,则添加1
,如果满足{,则减去(
{1}}。如果状态总是 1
,最后是 )
,那么它有正确的答案,这就是我们需要打印 >= 0
并返回的原因。
算法背后的想法是尝试字母 A、B 和 C 的所有可能性。含义 A 可以是 '(' 或 ')',B 和 C 的含义相同。
因此我们有 8 种可能性,
A B C in binary
( ( ( 0 0 0
( ( ) 0 0 1
( ) ( 0 1 0
( ) ) 0 1 1
) ( ( 1 0 0
) ( ) 1 0 1
) ) ( 1 1 0
) ) ) 1 1 1
实际上,作者说 '(' is 0 and ')' is 1,或相反,以二进制形式涵盖 8 种可能性,即从 000 到 111。为什么“或相反 em>”?因为正如您在上面看到的,如果一个掩码为 001
提供 A:(、B:( 和 C:),另一个掩码 110
将呈现 A:)、B:) 和 C:( .
因此,掩码从 0 到 8-1 ((1 << 3)-1
),或者,在二进制中,从 000 到 111。
例如,000 表示 A、B 和 C 都是左(或右)括号,而 110 在这种情况下是 ))(
,表示 ABC
(或 (()
)。
对于给定的掩码(外循环),内循环 for (int i = 0; i < n; i++)
遍历所有字符,并应用当前掩码。 (mask >> (int)(s[i] - 'A')) & 1
只是告诉,对于当前掩码,i 处的字母是 (
还是 )
。
也就是说,(int)(s[i] - 'A')
为 A 提供 0
,为 B 提供 1
,为 C 提供 2
。因此,A 获得位置 0 的位值(掩码的最右边位),B 是第 2nd 一位,C 是第 3rd(最左边)。
C B A
mask b2 b1 b0
变量 bal
可能是 balance。每次遇到“开始” ((
) 字母时,bal
都会递增,然后递减 )
。
内循环中的 ok &= bal >= 0
确保在任何时候我们都没有比左括号更多的结束。因此,这会阻止 ABBA
被限定。
内循环之后的 ok &= bal == 0
确保我们有相同数量的左括号和右括号。
同样,为了避免混淆,掩码尝试了所有可能性,因此 ABAB
不符合 )()(
,例如,掩码 001
,但随后掩码到达010
给出符合条件的 ()()
。
注意:掩码很可能从 001
(1) 开始到 110
(6) 结束,因为 000
(0) 和 {{1 }} (7) 值永远不会起作用(将所有 A、B 和 C 分配给同一个括号不起作用)。