P9574 「TAOI-2」Break Through the Barrier 题解
本题是一道找规律题目。
基本的思路是:
如果出现序列BTTB...
或者BTTBTBTB...TB
,就可以通过连续的向右操作将这两种序列之后连续的T
序列TTT...
的开头延长一个T
例如:
[BTTB]TBTBTTTTTT...
=>
TB[BTTB]TBTTTTTT...
=>
TBTB[BTTB]TTTTTT...
=>
TBTBTBBTTTTTTT...
(中括号代表每一次操作选定的序列)
同时,如果在一串T
序列...TTT
的结尾出现上面序列的对称形式,即...TTTBTTB
或者...TTTBT...BTBTBTTB
就可以将前面T的序列向后扩展一个T
。操作顺序与向前扩展序列的操作顺序正好相反。
思路是用一个有限状态机来维护上面的状态,识别出在连续的T
串头部和尾部的拓展序列。
Release 1.0 : 初步的思路
初步构建的有限状态机的状态转移图:
这个状态机共有十一个内部状态,其中
START
为初始状态,是所有状态机开始运行的状态,相应的,END
是终结状态,状态机将在这里结束运行。状态机有一个内部变量tot
,该变量被用于计算T
串中T
的数目并最终形成输出。在每一条路径上的第一个单词表示该路径的转移条件。转移条件一共有三种:接收到T
,接收到B
,接收到\n
(图中为EOF
)。一些路径在转移的同时会修改内部变量。tot x
意味着将内部变量tot
赋值为x
,out x
是状态机的输出,out max x,y
意味着输出x
和y
中较大的值,最终的输出为状态机在运行过程中的所有输出的最大值。
issue:
很可惜,hack掉这个状态机很简单。
BTTBTTBTTTTT
该序列可以通过如下修改:
BTT[BTTB]TTTTT
=>
BTTTBBTTTTTT
得到六个T
连续的序列,同时也是该样例的正解,但是我们构造的状态机会输出5
,因为状态机仅仅会将第二个BTTB
中间的T
序列识别为连续的T
,并不会将其识别为可以被修改的序列。当运行至状态..TBTT
时,再输入一个T
仅仅会使得状态机重新开始计算T
的数量,而忽略了该连续的T
串头部同样可以扩张的可能。
Release 2.0
重新考量状态..TBTT
之后的构造。不难发现,如果状态机处于该状态并且tot == 3
时,T
串的前方应当有一串BTTB
,此时T
串头部可扩展。故该状态的转移应当由外部条件和内部变量共同决定。 解决方案是仍不调整该点所转移到的状态,但是当tot == 3
时,将tot
的值赋为4
。
同时,在所有转移到状态tot++
的路径中,有且仅有从状态BTTBT..
和从前端可扩展的状态...TBTT
中转移过来的路径可以构造出BTTB
,故设一个新的内部变量flag
,在通过上述两条路径转入状态tot++
时将flag
设为1
,自己转入自己的时候该变量的值不变,其他的转入路径则将该变量设为0
。同时,如果状态处于..TBTT
,当且仅当tot==3
同时flag==1
的时候可以将tot
设为4
并转入状态tot++
。
最终的状态转移图:
请注意
..TBTT
返回tot++
的节点,仅当tot==3
并且flag==1
的时候将tot
修改为4
,这意味着序列前方存在一串BTTB
。
Accepted代码:
#include "bits/stdc++.h"
#define int long long
// 定义有限状态机的状态节点
#define START 0
#define B_ 1
#define BT 2
#define BTT 3
#define BTTB 4
#define BTTBT 5
#define TOT 6
#define TB 7
#define TBT 8
#define TBTT 9
#define END 10
using namespace std;
int max(int x, int y) { return x > y ? x : y; }
// 有限状态机的实现
class solve {
public:
int now;
int tot;
int Max;
int flag;
solve() {
this->now = START;
this->tot = 0;
this->Max = 0;
this->flag = 0;
}
void out(int x) { this->Max = max(Max, x); }
void fun(char ch) {
if (now == START) {
if (ch == 'B') {
now = B_;
return;
} else if (ch == 'T') {
flag = 0;
now = TOT;
tot = 1;
return;
} else {
now = END;
out(0);
return;
}
} else if (now == B_) {
if (ch == 'B') {
now = B_;
return;
} else if (ch == 'T') {
now = BT;
return;
} else {
now = END;
out(0);
return;
}
} else if (now == BT) {
if (ch == 'B') {
out(1);
now = B_;
return;
} else if (ch == 'T') {
now = BTT;
return;
} else {
now = END;
out(1);
return;
}
} else if (now == BTT) {
if (ch == 'B') {
now = BTTB;
return;
} else if (ch == 'T') {
tot = 3;
flag = 0;
now = TOT;
return;
} else {
now = END;
out(2);
return;
}
} else if (now == BTTB) {
if (ch == 'B') {
now = B_;
out(2);
return;
} else if (ch == 'T') {
now = BTTBT;
return;
} else {
now = END;
out(2);
return;
}
} else if (now == BTTBT) {
if (ch == 'B') {
now = BTTB;
return;
} else if (ch == 'T') {
flag = 1;
tot = 3;
now = TOT;
return;
} else {
out(2);
now = END;
return;
}
} else if (now == TOT) {
if (ch == 'B') {
now = TB;
return;
} else if (ch == 'T') {
now = TOT;
tot++;
return;
} else {
out(tot);
now = END;
return;
}
} else if (now == TB) {
if (ch == 'B') {
out(tot);
now = B_;
return;
} else if (ch == 'T') {
now = TBT;
return;
} else {
out(tot);
now = END;
return;
}
} else if (now == TBT) {
if (ch == 'B') {
now = TB;
return;
} else if (ch == 'T') {
now = TBTT;
return;
} else {
out(tot);
return;
}
} else if (now == TBTT) {
if (ch == 'B') {
out(tot + 1);
tot = 0;
now = BTTB;
return;
} else if (ch == 'T') {
out(max(tot, 2));
if (tot == 3 && flag == 1) {
flag = 1;
tot = 4;
} else {
flag = 0;
tot = 3;
}
now = TOT;
return;
} else {
out(max(tot, 2));
tot = 0;
now = END;
return;
}
} else {
}
}
};
int read() {
int x = 0, f = 1;
int c = getchar();
while (c > '9' || c < '0') {
if (c == '-') {
f = -1;
}
c = getchar();
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
signed main() {
int _ = read();
while (_--) {
int n = read();
char *chlist = new char[n + 1];
for (int i = 0; i < n; i++) {
scanf("%c", &(chlist[i]));
}
// 向字符串的结尾添加结束状态机的标识符
chlist[n] = '\n';
// 新建一个状态机
solve *S = new solve;
for (int i = 0; i <= n; i++) {
// 遍历整个字符串并转移状态机的状态。
// 当然,也可以一边读入一边遍历,这样会节省一些空间。
S->fun(chlist[i]);
}
cout << S->Max << endl;
}
return 0;
}
算法复杂度: O ( n ) O(n) O(n)。