打表
打表是一种以空间换时间的策略,下面举几个打表的形式。
1 在程序中一次性计算出所有结果,之后的查询直接采用这些结果。
2 在另一个程序中暴力计算出结果,再把程序存到数组里,直接使用。相当于暴力算法。
3 先暴力计算小范围数据的结果,然后根据规律找出解决问题的方法。
递推
如果题目中有递推关系的话,活用递推关系就可以降低时间复杂度。
例如,对于序列问题来说,假如序列中的每一位所需要计算的值都可以通过左右两侧的结果计算得到,那么就可以考虑左右两侧的结果能否通过递推得到,这样就能避免反复计算,从而降低时间复杂度。
下面来看一道题:
PAT B1040 有几个PAT
字符串 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,以它形成的PAT个数等于它左边的P的个数乘以它右边的T的个数。于是问题就转变为,对字符串中的每个A,计算它左边P的个数与右边T的个数的乘机,再把所有A的结果相加即可。
首先要获得所有左边P的个数。这里有一个方法,开一个数组leftp,然后如果当前位i的元素是P,那么leftp[i]等于leftp[i-1]+1,否则leftp[i]就等于leftp[i-1],这样这个数组就能记录每个位置,它左边的P的个数。
然后再考虑右边T的个数,从序列右边开始遍历,是T则用计数器记下来,遇到不是T且是A的时候,就查阅A左边的位置的leftp的值,然后再乘以计数器里的T。最后累加即可(记得取模)
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
const int mod = 1000000007;
char str[maxn];
int leftp[maxn] = {0};
int main(){
gets(str);
int len = strlen(str);
int i, ans = 0, tcount = 0;
for(i = 0; i < len; i++){//处理p的个数并存到leftp数组里
if(i>0){
leftp[i] = leftp[i-1];
}
if(str[i] == 'P'){
leftp[i] = leftp[i-1]+1;
}
}
for(i = len-1; i >= 0; i--){
if(str[i] == 'T')tcount++;
else if(str[i] == 'A'){
ans += leftp[i-1] * tcount;
ans = ans % mod;
}
}
printf("%d",ans);
return 0;
}
随机选择算法
先看这样一个问题:如何从一个无序的、没有相同元素的数组中找出第K大的数。
举个例子,对于数组{1,2,3,4,5,6}来说,5就是第5大的数。
最直接的算法是对数组排序然后选出第K大的元素,但这样做的时间复杂度是O(nlogn),而我们有更优解,可以让时间复杂度变为O(n)。
随机选择算法类似于随机快排算法。对于一个数组A,在[left,right]区间内应用该算法,则先利用randpartition(在我博客two pointers里有讲解到这个函数)选出一个位置p,那么A[p]左边全是比A[p]小的元素,则A[p]是该序列第p-left+1大的元素,不妨令M=p-left+1,如果K=M,那么A[p]即为结果;如果K<M,那么代表第K大的元素在A[p]左边,对左边递归;否则对A[p]右边递归。left==right即为递归边界。
下面来看代码:
int randSelect(int A[], int left, int right, int K){
if(left == right) return A[left];
int p = randpartition(A, left, right);
int M = p - left + 1;
if(K == M) return A[p];
if(K < M){
return randSelect(A, left, p-1, K);
}
else {
return randSelect(A, p+1, right, K-M);
}
}
随机选择算法的时间复杂度是O(n)。