《算法笔记》读书记录DAY_8

本文介绍算法入门中的两种技巧——打表与递推,并通过实例讲解如何运用这两种技巧来解决实际问题,提高算法效率。

CHAPTER_4  算法初步入门

4.7.1 打表

打表是一种空间换时间的技巧,一般指将所有可能用到的结果事先计算出来,这样在后面需要用到时直接查表即可。常见用法有如下几种:

(1)在程序中一次计算出所有可能用到的结果,之后的查询直接取这些结果

         例如在某个问题中我们需要用到大量斐波那契数。在用到第n个斐波那契数F(n)时我们需要计算F(n),但若我们需要大量用到F(1)、F(2)、...、F(n),每次从头计算显然很浪费时间。我们可以提前将可能用到的F(n)都计算并存储与数组中,在需要用到只需查询即可。

(2)对一些感觉不会做的题目,先用暴力程序计算小范围数据的结果,然后找规律,或许能发现一些蛛丝马迹。

4.7.2 活用递推

有很多题目需要细心考虑过程中是否存在递推关系,如果能找到递推关系,就能使时间复杂度下降不少。

题目:

字符串APPAPT中包含了两个"PAT"单词,其中第一个PAT由第二位、第四位和第六位字母组成的;第二个PAT是由第三位、第四位和第六位组成的。

现给定字符串(只包含P、A、T三个字母),问一共可以形成多少个PAT?

输入格式:

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

输出格式:

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

输入样例: 

APPAPT 

输出样例:

2

思路: 

对于一个确定位置的A来说,以它形成PAT的个数等于它左边P的个数乘以它右边T的个数。例如对于PPAT,它形成PAT的个数为2*1=2.。于是该问题转换为,对字符串中的每个A计算它左边P的个数与右边T的个数的乘积。之后把所有A的乘积相加。

那么有没有比较快获得每一位左边P个数的方法呢?当然有,我们可以设置一个数组leftNumP,记录每一位左边P的个数,接着从左到右遍历字符串,如果当前位i是P,那么令leftNumP[i]=leftNumP[i-1]+1;如果当前位i不是P,那么令leftNumP[i]=leftNumP[i-1]。

同样的,我们使用rightNumP记录每一位右边T的个数。从右到左遍历字符串,如果当前位i是T,那么令rightNumP[i-1]=rightNumP[i]+1;如果当前位i不是T,那么令rightNumP[i-1]=rightNumP[i]。

最后,我们遍历字符串。如果当前位是A,我们计算leftNumP[i]*rightNumP[i],并累加至总数。遍历完后将总数对1000000007取余数,输出。

参考代码:

#include<iostream>
#include<string>
using namespace std;

const int MOD=1000000007;

int main() {
	string str;
	cin>>str;
	int len=str.size(),count=0;               //获取字符串长,创建count用于存储最终结果 
	int* leftNumP=new int [len]; 
	int* rightNumT=new int [len];             //创建和字符串等长的数组leftNumP、rightNumT
	leftNumP[0]=(str[0]=='P')?1:0;	          //给leftNumP第一位赋值
	for(int i=1;i<len;i++) {
		if(str[i]=='P')
			leftNumP[i]=leftNumP[i-1]+1;
		else
			leftNumP[i]=leftNumP[i-1];		
	}                                          //按上述算法给leftNumP赋值 
	rightNumT[len-1]=(str[len-1]=='T')?1:0;	   //给rightNumT[最后一位赋值
	for(int i=len-2;i>=0;i--) {
		if(str[i]=='T')
			rightNumT[i]=rightNumT[i+1]+1;
		else
			rightNumT[i]=rightNumT[i+1];		
	}                                          //按上述算法给rightNumT赋值 
	for(int i=0;i<len;i++) {
		if(str[i]=='A')
			count+=leftNumP[i]*rightNumT[i];
	}                                          //遍历str计算最后结果 
	cout<<count%MOD<<endl;
	return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值