分析题意
得到"答案正确"的条件是:
字符串中必须仅有 P、 A、 T这三种字符,不可以包含其它字符;
任意形如 xPATx 的字符串都可以获得"答案正确",其中 x 或者是空字符串,或者是仅由字母 A 组成的字符串;
如果 aPbTc 是正确的,那么 aPbATca 也是正确的,其中 a、 b、 c 均或者是空字符串,或者是仅由字母 A 组成的字符串。
根据题目中的规则,如果aPbTc是正确的,那么aPbATca也是正确的。这里我们可以把字符串中P之前的A的个数看作x,P和T之间的A的个数看作y,T之后的A的个数看作z。
对于初始的合法字符串xPATx,这里x可以为空字符串或者仅由A组成的字符串,此时有x个A在P之前,1个A在P和T之间,x个A在T之后,满足x * 1=x的关系。
当按照规则扩展时,比如从aPbTc变为aPbATca,如果P之前A的个数为a,P和T之间A的个数从b变为b + 1,T之后A的个数从c变为c + a。这种变化始终保持着a * b=c的关系(在原始的xPATx中就是x * 1=x)。
总之就是:只有P,A,T三种字符,PT之间必须有A,P前面的A * PT之间的A == T之后的A
代码思路
确定字符种类
要检测只有三种字符,还有统计其中A,P,T的个数,可以想到利用键值对map,分别检测字符和字符数量,那么就创建一个map<char ,int>类型的键值对变量m。
我们就可以通过遍历字符串的每个字符的方式,来确定字符种类数,和每种字符的个数。
确定A的数量关系
要确定A的数量关系,可以利用下标(索引)来求,我们可以发现字符串索引从0开始,那么P之前A的个数就可以用P的下标来代表,那么我们在循环中一旦遍历到'P',我们就把它的下标存到一个变量p中作为P前面A的个数。
PT之间
T的索引 - P的索引再减去1.比如
PTA
012
2 - 0 - 1 = 1,1为PT间A的个数
T之后A的个数
字符串长度 - T的索引 - 1
PTAAAA
0123456
6 - 1 - 1 = 4为T之后A的个数
P和T之间必须有A
那么P和T的索引之差就不能小于1
只有P,A,T三种字符
那么m.size() == 3,如果还有多的或者是不足三种,m.size()就不为3了
m.size()代表的是这个键值对中字符种类的个数
P,T个数只有一个
那么,m['P'] == 1, m['T'] == 1
代码实现
cpp
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
string s;
int n = 0;
int t = 0;
int p = 0;
cin >> n;
int i = 0;
for (i = 0; i < n; i++)
{
cin >> s;
map <char, int> m;
int j = 0;
for (j = 0; j < s.size(); j++)
{
m[s[j]]++;//统计字符种类数,以及每种字符的个数
if (s[j] == 'P')
{
p = j;
}
if (s[j] == 'T')
{
t = j;
}
}
if (m['T'] == 1 && //T只有一个
m['P'] == 1 && //P只有一个
m.size() == 3&& //只有P,A,T三种字符
t - p > 1 && //P和T之间必须有A
m['A'] != 0 &&//A的个数不为0
p * (t - p - 1) == s.length() - t - 1//P前的A * PT间的A == T后的A
)
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
}
return 0;
}
自己写的错误
将if (s[j] == 'P')写成if (m[s[j]]=='P')
分析
- 原意
- 当代码为
if (s[j]=='P') p = j;
时,它的原意是检查字符串s
中的第j
个字符是否为'P'
。如果是,就将j
的值赋给变量p
,这里p
很可能是用来记录字符'P'
在字符串中的位置索引。
- 当代码为
- 错误写法的影响及区别
- 当写成
if (m[s[j]]=='P') p = j;
时:- 首先,
m
是一个map<char, int>
类型,m[s[j]]
会返回一个int
类型的值(这个值是字符s[j]
在m
中的计数)。而'P'
是一个字符常量。在C++ 中,将一个int
类型的值与一个字符常量进行比较是类型不匹配的操作。 - 这种错误写法会导致程序逻辑混乱。因为它不是在检查字符串中的字符是否为
'P'
,而是在检查字符s[j]
在m
中的计数是否等于字符'P'
的ASCII码值(实际上由于类型不匹配,这种比较没有实际意义)。这与原代码的功能完全不同,原代码是在寻找字符串中'P'
字符的位置,而错误写法是在对一个不相关的计数进行无意义的比较。
- 首先,
- 当写成
map<char, int> m不放入循环
分析
- 作用域与数据复用问题
- 如果将
map<char, int> m;
放在循环外面,每次循环不会重新创建一个新的m
。 - 这可能导致数据复用问题。例如,在处理多个不同的输入字符串时,
m
会累积之前字符串中字符的计数信息。如果第一个字符串是"PAT",处理完后m['P'] = 1
,m['A'] = 1
,m['T'] = 1
。当处理下一个字符串时,m
已经有了之前的计数,这会干扰对新字符串中字符的正确统计。
- 如果将
- 逻辑错误
- 从逻辑上讲,程序可能会得到错误的结果。因为对于每个输入字符串,都应该独立地统计其中字符的出现次数。如果
m
在循环外,就无法保证每个字符串的统计是独立的。例如,程序可能会错误地判断字符出现的次数或者满足某些条件的情况,如判断是否只有特定的字符(P
、A
、T
)以及它们的数量关系是否符合要求等都会因为m
的复用而得出错误的结论。
- 从逻辑上讲,程序可能会得到错误的结果。因为对于每个输入字符串,都应该独立地统计其中字符的出现次数。如果
漏写m[s[j]]++;
-
代码目的
- 在这段代码中,
m[s[j]]++;
这行代码的目的是统计字符串中每个字符出现的次数。 - 这里
m
是一个map<char, int>
类型的容器,它的键是字符类型(char
),值是整数类型(int
)。 - 当执行
m[s[j]]++;
时:- 如果
s[j]
这个字符是第一次出现在字符串中,那么m[s[j]]
会创建一个新的键值对,键为s[j]
,值初始化为0,然后执行++
操作后,这个字符对应的计数就变为1,表示这个字符出现了1次。 - 如果
s[j]
这个字符已经在之前出现过,那么m[s[j]]
会直接找到对应的键值对,然后执行++
操作,将这个字符的计数加1,表示这个字符又出现了一次。
- 如果
- 这样做是为了后续判断字符串中是否只有
P
、A
、T
三种字符(通过m.size()==3
判断)以及判断P
、A
、T
的数量是否符合要求(例如m['P'] == 1
等)。
- 在这段代码中,
-
计数功能缺失
- 如果没有
m[s[j]]++;
这行代码,首先会导致字符计数功能无法实现。 - 假设
m
是用于统计字符串中字符出现次数的map
,原本这行代码会根据字符串中的字符s[j]
,在m
中找到对应的键(如果不存在则创建),并将其对应的值加1。没有这行代码,就不能对字符串中的字符进行计数。
- 如果没有
-
后续判断错误
- 在程序后续可能存在对字符出现次数的判断逻辑。例如,可能需要判断某个字符是否只出现了特定的次数,或者判断不同字符出现次数之间的关系。
- 由于没有正确统计字符出现次数,这些判断都会得出错误的结果。例如,如果需要判断字符串中是否只有特定的三种字符(如
P
、A
、T
)且数量关系符合某种规则,由于没有准确的计数,可能会错误地认为字符串满足或不满足条件。