1003 我要通过! (20分)

答案正确”是自动判题系统给出的最令人欢喜的回复。本题属于 PAT 的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案错误”。

得到“答案正确”的条件是:

  1. 字符串中必须仅有 PAT这三种字符,不可以包含其它字符;
  2. 任意形如 xPATx 的字符串都可以获得“答案正确”,其中 x 或者是空字符串,或者是仅由字母 A 组成的字符串;
  3. 如果 aPbTc 是正确的,那么 aPbATca 也是正确的,其中 abc 均或者是空字符串,或者是仅由字母 A 组成的字符串。

现在就请你为 PAT 写一个自动裁判程序,判定哪些字符串是可以获得“答案正确”的。

输入格式:

每个测试输入包含 1 个测试用例。第 1 行给出一个正整数 n (≤10),是需要检测的字符串个数。接下来每个字符串占一行,字符串长度不超过 100,且不包含空格。

输出格式:

每个字符串的检测结果占一行,如果该字符串可以获得“答案正确”,则输出 YES,否则输出 NO

输入样例:

1
2
3
4
5
6
7
8
9
10
11
10
PAT
PAAT
AAPATAA
AAPAATAAAA
xPATx
PT
Whatever
APAAATAA
APT
APATTAA

输出样例:

1
2
3
4
5
6
7
8
9
10
YES
YES
YES
YES
NO
NO
NO
NO
NO
NO

思路:

这道题说难也不是很难,说简单也不简单,主要是要理解如何判断一个字符串是否正确。接下来我们一条一条的看:

  1. 字符串中必须仅有 PAT这三种字符,不可以包含其它字符;
    由此可以得出,字符串的组成只有PAT这三种字符,一旦出现其他的字符可以直接判断NO。那么用什么结构来存储输入的字符串,以及更严格的要求,还得看接下来的两个条件。

  2. 任意形如 xPATx 的字符串都可以获得“答案正确”,其中 x 或者是空字符串,或者是仅由字母 A 组成的字符串;
    这里x其实就相当于未知数,也就是说PAT这个字符串前后可以接受 加上两个相等的字符串(要么为空要么全是A) ,形如这样的字符串都可以判断YES
    图片1
    那么问题来了, 如何在代码中判断这个PAT前后加的两个字符串是否符合要求呢? 在这里,C++提供的 string 其实就可以满足了,遍历string容器,确定字符P和字符T的位置,就可以很方便的判断前后字符串是否符合要求。

  3. 如果 aPbTc 是正确的,那么 aPbATca 也是正确的,其中 abc 均或者是空字符串,或者是仅由字母 A 组成的字符串。
    这里是最复杂的,我们慢慢分析。这里有一个大前提就是:如果 aPbTc 是正确的。那以我们现在的要求,要想让aPbTc是正确的,那么PT中间的b肯定是字符串A,而且ac要相等才行,那么就可以代入一下第二个要求的图片。

    ①. a=c=‘∅’(a和c是空串),b=A。再看 那么 aPbATca 也是正确的 ,那这个字符串就变成了PAAT,由题意知,这个也是对的。那如此嵌套循环下去,PAAATPAAAAT……等等也是正确的。因为只要是正确的字符串,就可以继续套入 aPbATca 这个“公式”。用人话说就是: PT之间至少有一个 A

    ②. a=c=‘A’,b=‘A’。代入公式得APAATAA,现在字符串就有点奇怪了,好像看不出什么规律,那么我们接着往下看。
    图片2

    ③. a=c=“AA”,b=‘A’。代入公式得AAPAATAAAA,这下是不是有点思路了,这是不是 (P前面的A) × (PT中间的A) = (T后面的A)呢 ?不确定?那我们再随机验证一下。
    图片3

    ④. a=‘A’ c=“AA”,b=‘AA’。这次ac不相等了,我们再代入一次公式,得到了APAAATAAA,现在就确定了,就是 (P前面的A) × (PT中间的A) = (T后面的A)
    2E08DDB156D1F7852178DC1CE6443BF7

    其实同样的规律也可以用于第二个要求,因为中间只有一个A,那么不管两边有多少个A,只要数量相等就可以,因为1乘任意数就等于任意数嘛。但是这里还有一个坑,三个要求全部看完之后,其实还有一些条件我们没有发现,接下来我们去看看输入样例和输出样例。

  4. 在样例中,xPATx、PT、Whatever、APAAATAA、APT、APATTAA都是错的,其中xPATx、Whatever都是因为包含有其他字符而错误。PT和APT是因为第三条的第一点,PT之间至少有一个A,所以PT错。APAAATAA这是因为A之间的关系不满足,所以也是错的。那么这个APATTAA是因为什么错呢,虽然说这个字符串A之间的关系也不满足(因为1 × 1 应该为 1 才对),但是还有一个致命的错误那就是 有两个T ,导致我们无法判断是哪个T的前后,包括P也是一样,所以这里应该还有一个条件是: 字母P和字母T只能有一个

  5. 现在所有的条件都齐全了,那么在代码上由于要判断这个字符串所包含字符的种类、数量及位置,这里我想到用 map容器 来存储这个字符串的信息。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <map>
#include <string>
using namespace std;

int main()
{
int n;
cin >> n; //输入字符串的个数
string s;
for (int i = 0; i < n; i++)
{
cin >> s; //循环输入字符串
map<char, int> m; //对组第一个表示字符,第二个表示其对应的数量
int p = 0, t = 0; //利用整型变量存储P和T的位置
for (int j = 0; j < s.size(); j++)
{
m[s[j]]++; //储存各字符的数量信息
if (s[j] == 'P') //如果遍历到P了,就把P的位置记录下来
p = j;
if (s[j] == 'T') //如果遍历到T了,就把T的位置记录下来
t = j;
}
if (m.size() == 3 && m['T'] == 1 && m['P'] == 1 && (t - p > 1) && m['A'] != 0 && p * (t - p - 1) == (s.size() - 1 - t))
/*
1.只能有PAT三个字母
2.字母T只能有一个
3.字母P只能有一个
4.T和P中间起码有一个A
5.A的数量不能为0
6.P前面A的数量 × P和T之间A的数量 == T后面A的数量
*/
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
}

return 0;
}