题目(PAT 1003 我要通过!)
“答案正确”是自动判题系统给出的最令人欢喜的回复。本题属于 PAT 的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案错误”。
得到“答案正确”的条件是:
- 字符串中必须仅有
P
、A
、T
这三种字符,不可以包含其它字符; - 任意形如
xPATx
的字符串都可以获得“答案正确”,其中x
或者是空字符串,或者是仅由字母A
组成的字符串; - 如果
aPbTc
是正确的,那么aPbATca
也是正确的,其中a
、b
、c
均或者是空字符串,或者是仅由字母A
组成的字符串。
现在就请你为 PAT 写一个自动裁判程序,判定哪些字符串是可以获得“答案正确”的。
输入格式:
每个测试输入包含 1 个测试用例。第 1 行给出一个正整数 n (≤10),是需要检测的字符串个数。接下来每个字符串占一行,字符串长度不超过 100,且不包含空格。
输出格式:
每个字符串的检测结果占一行,如果该字符串可以获得“答案正确”,则输出 YES
,否则输出 NO
。
输入样例:
10
PAT
PAAT
AAPATAA
AAPAATAAAA
xPATx
PT
Whatever
APAAATAA
APT
APATTAA
输出样例:
YES
YES
YES
YES
NO
NO
NO
NO
NO
NO
题目解读(划重点):
我相信有很多同学直到提交程序测试不通过才回过头发现自己其实根本没有读懂题目,其实我也是,所以想在这里写点东西加深印象。
判读字符串是否合法一共有三个条件:
第一个条件为基础条件,循环判断所有字符即可;
第二个条件为直接合法的一种字符串格式,为xPATx,其中x为空字符或者只包含A字符的字符串,因此类似PAT,APATA,AAPATAA这样的字符串都是直接可以判定合法的字符串(注意这里首尾的A字符串长度需要保持一致,因为都是x);
第三个条件比较有意思,没有直接给出判定规则,描述类似于一种递推的形式,如果 aPbTc
是正确的,那么 aPbATca
也是正确的,那么aPbTc要么是满足第二个条件的直接合法的字符串,要么就是直接合法的字符串根据第三个条件递推出来的合法字符串。
看到这里我们应该有一点似曾相识的感觉了,是不是跟我们最开始接触递推递归时的斐波那契数列很像,给定基础值和递推条件,我们可以一直往后填写整个数列的值。同样,这一题,我们也可以利用这样的思路来反向迭代来判断字符串是否合法。于是我们的解题思路如下:
首先判断是否满足第一个条件,不满足直接判定不合法;其次判定是否属于第二个条件的合法字符串,是则直接判定合法,不是就再看第三个条件;如果不满足aPbATca的形式,直接判定非法,如果满足,则只需判断对应的aPbTc是否合法,需要做的就是在字符串末尾删除a以及删除T之前的一个A字符。只要通过一直往前迭代知道能够判断缩减后的字符串是否满足第二个条件,即可判断整个字符串是否为合法字符串了。
于是我们的编程思路也就确定了,写一个判断字符串是否满足一二条件直接合法的子函数,然后在主函数中通过迭代的方式缩减原字符串,不断去判断是否满足xPATx形式,直至其不满足aPbATca形式为止。(因为xPATx也符合aPbATca形式,所以需要在迭代缩减的过程中不断去判断是否合法)
代码实现如下(C语言):
#include <stdio.h>
#include <string.h>
int xPATx_judge(char *s){//判断是否为满足xPATx的字符串
int l = strlen(s);
if(l%2==0){
return 0;//xPATx格式长度必然为奇数
}
int len_x = (l-3)/2;//xPATx基础合法格式中x的长度
for(int i=0; i<len_x; i++){
if(s[i]!='A'||s[i+len_x+3]!='A'){//首尾一起检查
return 0;
}
}
if(s[len_x]!='P'||s[len_x+1]!='A'||s[len_x+2]!='T'){//中间必为PAT
return 0;
}
return 1;//1为合法
}
int aPbATca_judge(char *s){//判断是否满足aPbATca并缩减为aPbTc
int l = strlen(s);
if(l<=3){
return 0;
}
int loc_P = strchr(s, 'P')-s;
int loc_T = strchr(s, 'T')-s;
int len_a = loc_P;
if((l-loc_T-1)>=len_a&&s[loc_T-1]=='A'){
s[l-len_a]=0;//删除末尾的a字符串
strcpy(s+loc_T-1,s+loc_T);//删除A,T及之后的字符串向前移一位
return 1;
}
else{
return 0;
}
}
int main(){
int num;
scanf("%d", &num);
for(int i=0; i<num; i++){
char s[101];
scanf("%s", s);
//printf(" %s",s);
int num_P = 0;
int num_A = 0;
int num_T = 0;
int flag = 1;
//遍历一遍,判断基础非法情况
for(int j=0; s[j]; j++){
if(s[j]=='P'){
num_P++;
}
else if(s[j]=='A'){
num_A++;
}
else if(s[j]=='T'){
num_T++;
}
else{
flag = 0;//不满足条件一,非法
}
}
if(num_P!=1||num_T!=1||num_A==0){
flag = 0;//基础判断,P和T有且仅有一个,A数目大于等于一
}
if(flag==0){
printf("NO");//非法直接输出
goto PRINT_ENTER;
}
do{
if(xPATx_judge(s)){
//printf(" %s",s);
printf("YES");//合法直接输出
goto PRINT_ENTER;
}
}while(aPbATca_judge(s));//aPbATca向aPbTc格式缩减
flag = xPATx_judge(s);
if(flag){
printf("YES");
}
else{
printf("NO");
}
PRINT_ENTER:
if(i!=num-1){//打回车条件判断
printf("\n");
}
}
return 0;
}