题目
题意:给定一个只包含A B字符的字符串,问该字符串能否刚好由a个’A’, b个’B’, c个’AB’, d个‘BA’拼凑而成。
思路:容易想到的是bfs暴搜,嗯,,T了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200010;
int a, b, c, d, n;
char s[maxn];
struct node {
int a, b, c, d;
int pos;
node(int _a, int _b, int _c, int _d, int _p) {
a = _a;
b = _b;
c = _c;
d = _d;
pos = _p;
}
bool operator < (const node & h) const {
if (a == h.a) {
if (b == h.b) {
if (c == h.c) {
if (d == h.d) {
return pos < h.pos;
}
return d < h.d;
}
return c < h.c;
}
return b < h.b;
}
return a < h.a;
}
void print() {
printf("%d %d %d %d (%d)\n", a, b, c, d, pos);
}
};
multiset<node> st;
void solve() {
scanf("%d%d%d%d", &a, &b, &c, &d);
scanf("%s", s);
n = strlen(s);
bool flag = 0;
st.clear();
queue<node> q;
q.push(node(0, 0, 0, 0, 0));
// printf("s :%s\n", s);// debug
while (!q.empty()) {
node cur = q.front();
q.pop();
if (st.find(cur) != st.end()) {
continue;
}
st.insert(cur);
// cur.print();// debug
if (cur.a == a && cur.b == b &&
cur.c == c && cur.d == d &&
cur.pos == n) {
flag = 1;
break;
}
if (cur.pos == n) {
continue;
}
if (s[cur.pos] == 'A' && cur.a < a) {
node tmp = cur;
++tmp.pos;
++tmp.a;
q.push(tmp);
}
if (s[cur.pos] == 'B' && cur.b < b) {
node tmp = cur;
++tmp.pos;
++tmp.b;
q.push(tmp);
}
if (cur.pos + 1 < n && cur.c < c &&
s[cur.pos] == 'A' && s[cur.pos+1] == 'B') {
node tmp = cur;
tmp.pos += 2;
++tmp.c;
q.push(tmp);
}
if (cur.pos + 1 < n && cur.d < d &&
s[cur.pos] == 'B' && s[cur.pos+1] == 'A') {
node tmp = cur;
tmp.pos += 2;
++tmp.d;
q.push(tmp);
}
}
printf("%s\n", flag ? "Yes" : "No");
}
int main() {
int t;
scanf("%d", &t);
// t = 1;
while (t--) {
solve();
}
}
/*
*/
- 首先,可以关注字符数是否一致,即A 和 B 的数量是否一致。
- 其次,只需关注能否匹配 c个’AB’,d个’BA’。我们把愿字符串拆分出来,对于出现相邻相等的部分,必然不可以用来拼接AB和BA,因此,我们可以把原字符串拆分成若干个相邻位置不相同的子字符串,即"ABA…“或"BAB…”。
子字符串的分配,也有讲究。
- 对于子字符串,如果它长度len为奇数,那么它可以组成x个AB,y个BA,只要x+y==len/2。如ABABA,它可以组成2 AB , 2BA,或者AB + BA。
- 如果它长度为偶数,且以AB开头,那么我们可以优先分配给AB类型的。因为这种场景下,不浪费字符,如果先分配给BA类型,则会引起字符浪费。如ABAB,它可以分配2AB,却只能分配1 BA。
- 如果它长度为偶数,且以BA开头,优先分配给BA类型的。原因同上。
按照这种思路去分配就够了吗,并不。我们还需要遵循短字符串优先分配的原则。就是相同类型的字符串,我们要优先消费短的。为啥?
a,b,c,d取 1 1 2 3
字符串取 ABABABBAABAB
按照上诉思路,我们拆分字符串为ABABAB, BA, ABAB这3种子串。
如果先消费ABABAB,由于优先分配给AB,ABABAB剩下AB,再分配给BA,此时贡献0个BA。后边的BA,ABAB总共贡献BA个数为2,不能满足要求。
而如果先消费ABAB, 由于优先分配给AB,ABAB刚好分配2个AB。后边ABABAB, BA再去分配BA,就有3个了,可以满足要求。
我们发现,优先消费短字符串,可以让更长的字符串给另一种类型做消费
因此,第4个消费逻辑,优先消费短的子字符串。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200010;
int a, b, c, d, n;
char s[maxn];
// 判断 A B字符数是否一致
bool check() {
int numa = 0;
for (int i = 0; i < n; ++i) {
if (s[i] == 'A') {
++numa;
}
}
return numa == a + c + d;
}
// 消费字符串,优先给fir,其次给sec
void cal(int &fir, int x, int &sec) {
if (fir >= x) {
fir -= x;
} else {
x -= fir + 1;
fir = 0;
sec -= min(sec, x);
}
}
void solve() {
scanf("%d%d%d%d", &a, &b, &c, &d);
scanf("%s", s);
n = strlen(s);
if (!check()) {
printf("No\n");
return;
}
int co = 0;
int i = 0;
vector<int> vc, vd;
while (i < n) {
int j = i + 1;
while (j < n && s[j] != s[j-1]) ++j;
int len = j - i;// 子字符串长度
if (len == 1) {
i = j;
continue;
}
if (len & 1) {// 奇数字段,可任意取AB或BA, 提取到公共计数
co += len / 2;
} else {// 偶数字段
len /= 2;
if (s[i] == 'A') {
vc.push_back(len);
// cal(c, len, d);
} else {
vd.push_back(len);
// cal(d, len, c);
}
}
i = j;
}
// 从小到大排序,优先消费短的
sort(vc.begin(), vc.end());
sort(vd.begin(), vd.end());
for (auto len: vc) {
cal(c, len, d);
}
for (auto len: vd) {
cal(d, len, c);
}
// printf("(%d, %d, %d)\n", co, c, d);
printf("%s\n", co >= c + d ? "Yes" : "No");
}
int main() {
int t;
scanf("%d", &t);
// t = 1;
while (t--) {
solve();
}
}
/*
*/