PAT 1040. 有几个PAT(25)-本文介绍三种方法

该博客探讨了如何解决PAT计数问题,避免暴力解法导致的时间超限。提出了三种方法,从数组处理到优化的数据结构,逐步提高算法效率,确保所有测试点都能通过。重点在于以A为中心,计算左右P和T的数量,快速确定PAT组合数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

字符串APPAPT中包含了两个单词“PAT”,其中第一个PAT是第2位(P),第4位(A),第6位(T);第二个PAT是第3位(P),第4位(A),第6位(T)。

现给定字符串,问一共可以形成多少个PAT?

输入格式:

输入只有一行,包含一个字符串,长度不超过105,只包含P、A、T三种字母。

输出格式:

在一行中输出给定字符串中包含多少个PAT。由于结果可能比较大,只输出对1000000007取余数的结果。

输入样例:
APPAPT
输出样例:
2

题目分析:

我会在下面介绍三种方法,其实这道题做之前需要进行思考,直接暴力解题的话必然超时。必须用巧方法。我们以A作为中心来看,每一个出现的A能组成PAT的个数就等于左边出现的P的个数乘以右边出现的T的个数。然后所有的A组成的PAT的个数相加就是最后的结果了。而最关键的就是该用最短的时间实现这种想法。最粗暴的方式不就写了。下面先写第一种方法,但是这个方法仍然不够快。

1.设定一个数组leftnumP[i]来记录第i个位置前有多少个P,如果当前位置的字母是P,那么leftnumP[i]=leftnumP[i-1]+1,如果不是leftnumP[i]=leftnumP[i-1].同样的方法来处理T,但是在处理的过程中遇到A,则将这个A对应的PAT个数求出来。请看代码:

#include<stdio.h>
#include<string.h>
const int maxn=100000;
const int mod=1000000007;               //用于取模的常数 
int leftnumP[maxn]={0},ans=0,rightnumT=0;   //rightnumT用统计T出现的次数 
  char str[maxn];
int main()
{
  
  gets(str);              //读入字符串 
  for(int i=0;i<strlen(str);i++){
    if(i>0){
      leftnumP[i]=leftnumP[i-1];    //先从前一位置继承过来 
    }
    if(str[i]=='P'){               //如果该位置是P,那么在此基础上在加一 
      leftnumP[i]++;
    }
  }
  for(int i=strlen(str)-1;i>=0;i--){
    if(str[i]=='T'){                 //从后遍历字符串,如果是T则rightnumT加一 
      rightnumT++;
    }
    else if(str[i]=='A'){           //遇到一个A,立马将它对应的PAT数目求出并加入到最终的答案 
      ans=(ans+leftnumP[i]*rightnumT)%mod;
    }
  }
  printf("%d",ans);                   //输出 
  return 0;
 } 

使用这种方法仍不够快,最后提交时仍然会有测试点超时。

2.使用数组,在进行读取数组的内容是仍会耗费时间,下面不使用数组。并且改用读取元素更快的string代替字符数组

#include<iostream>
#include<string.h>
using namespace std;
const int maxn=100000;
const int mod=1000000007;
int leftnumP=0,ans=0,rightnumT=0;   //用于统计P的leftnumP,最终的答案ans,统计T的rightnumT 
string str;            //使用string,要比字符数组的计算速度更快 
int main()
{
	cin>>str;           //读入字符串                   
	for(int i=0;i<str.length();i++){
		if(str[i]=='P'){
			leftnumP++;           //遍历统计所有的P出现的次数。 
		}
	}
	for(int i=str.length() -1;i>=0;i--){    //从后开始重新遍历字符串 
		if(str[i]=='T'){ 
			rightnumT++;      //遇见T,计数器加一 
		}
		if(str[i]=='P'){      //遇见P,则该位置左边的字符串中P的个数又少了一个。计数器减一 
			leftnumP--; 
		}  
		if(str[i]=='A'){        //一旦遇见A,此时的rightnumT正是这个A右边T的个数,liftnumP是这个A左边P的个数 
			ans=(ans+leftnumP*rightnumT)%mod;   //计算这个A对应的所有PAT的个数并计入到最终结果中 
		}                        
	}
	printf("%d",ans);   //输出 
	return 0;
 } 

使用这种方法更快,能通过所有的测试点。

3.这种方法更加快捷和简单,运行速度要提高一倍。我们不妨将目光放在T上,每个T能形成PAT的数目要看其之前能有多少个PA,而这些PA的数量又是每个A对应形成的PA的求和,每个A能形成多少PA由这个A之前有多少个P决定。所以请看下面的代码:

#include<stdio.h>
#include<string.h>
const int maxn=100000;
const int mod=1000000007;               //用于取模的常数 
int main()
{
	int Pnum=0,PAnum=0,PATnum=0;
	char letter;
	while(1){
		letter=getchar();
		if(letter=='\n'){       //如果读到换行符,则终止循环。 
			break;
		}
		if(letter=='P'){
			Pnum++;            //出现P的次数 
		}
		if(letter=='A'){   
			PAnum=(PAnum+Pnum)%mod;   //这个A能形成的PA的数目是它之前的P出现的次数,并将它加入到对所有PA的汇总中 
		}
		if(letter=='T'){
			PATnum=(PATnum+PAnum)%mod;
		}
	
	} 
  printf("%d",PATnum);                   //输出 
  return 0;
 } 

我突然发现使用while循环和getchar()结合,可以快速的处理字符串问题。哈哈哈哈。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值