PAT-B 1040. 有几个PAT(25)

来源:互联网 发布:淘宝装修店铺视频教程 编辑:程序博客网 时间:2024/05/01 21:43

题目链接在此。

和《算法笔记》内容稍稍不同,下次记录《算法笔记》中的表达。


来了,今天用《算法笔记》的方法实现了一遍,便于理解和总结“打表+递推将时间复杂度减少到O(N)法”的说法(我自己编的……)。
ps:思路一相关是上次的,思路二是《算法笔记》上过来的。

思路

思路1

暴力肯定是要超时的。然而对于一个确定的A来数,以它形成PAT的个数是这个A前面的P的个数以及这个A后面的T的个数的乘积。于是只需要知道所有A之前P的个数以及所有A之后T的个数,然后对于每个A进行前P后T个数乘积累加起来就是答案。

至于如何操作,用numP数组保存每个A之前的P的总个数,即numP[i] = j表示第i个A之前的P的个数为j个;用numT[i]表示第i+1个A(或是输入字符串末端)之前的所有T的个数,用t表示总共有t个A的话,numT[t]-numT[i]求的就是第i个A之后P的个数了。

思路二

思路一真的是真真实实的用’A’来拆分了输入的字符串啊~其实有更加通用的方法,那就是记录每一位左边’P’的个数以及右边’T’的个数(包括自身这一位)。

有了思路一的基础,直接看代码吧。
看完和这一题对照,会发现这个方法无比的好用。之前自己叫它们“用数组表示前n为和”,其实归根结底还是“递推”的思想,现在终于有了更贴切的名字——“打表+递推将时间复杂度减少到O(N)法”。

AC代码

思路一代码

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;char str[100010]; //保存读入的字符串 long long numP[100010] = {0}, p = 0; //numP[i]表示第i个A之前'P'的个数,p为numP下标 long long numT[100010] = {0}, t = 0; //numT[i]表示第i+1个A之前'T'的个数,t为numT下标 long long sum = 0; //保存结果 int main(){    gets(str);    int len = strlen(str);    int count = 0; //记录A的个数     for(int i = 0; i < len; i++){        if(str[i] == 'P'){ //如果str[i] == 'P'             numP[p]++;  //第p个A之前的'P'的数目+1         }else if(str[i] == 'T'){  //如果str[i] == 'T'             numT[t]++; //第t+1个A之前的'T'的数目+1        }else{  //str[i] == 'A'            count++; // A的个数+1             p++; //p+1,开始记录下一个A之前P的个数了             t++; //t+1, 开始记录下一个A之前T的个数了             numP[p] += numP[p-1]; //累加之前的P的个数             numT[t] += numT[t-1]; //累加之前的T的个数         }    }    for(int j = 0 ; j < count ; j++){ //有多少个A算多少种情况         sum += (numP[j] * (numT[t]-numT[j])); // numP[j]表示第j个A之前有多少个,numT[t]-numT[j] 表示第j个A之后有多少个T     }    printf("%lld\n",sum%1000000007);    return 0;}

思路二代码

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;char str[100010]; //保存输入字符串 int leftP[100010], rightT[100010]; //leftP[i]表示i左侧p的个数,rightT[i]表示i右侧T的个数 int main(){    gets(str);    int n = strlen(str);    //填充leftP     for(int i = 0; i <= n; i++){        if(i > 0){ //不是首位             leftP[i] = leftP[i-1]; //继承上一位的结果         }        if(str[i] == 'P'){ //这一位是'P'             leftP[i]++; //leftP[i]++         }    }     //填充rightT    for(int i = n-1; i >= 0; i--){        if(i < n-1){ //不是最后一位             rightT[i] = rightT[i+1]; //继承前一位的结果         }        if(str[i] == 'T'){ //如果当前位为'T'             rightT[i]++; //rightT[i]+1         }    }     //计算结果并输出     long long sum = 0;  //保存结果     for(int i = 0; i < n; i++){        if(str[i] == 'A'){ //如果当前为是A             sum += ((leftP[i] * rightT[i])); //则sum加上当前为前面P的个数和后面T的个数的乘积         }    }    printf("%d\n",sum%1000000007); //记得模完输出     return 0;}

当然,也可以不填充rightT,直接在找rightT的过程中累加答案:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;char str[100010]; //保存输入字符串 int leftP[100010], rightT[100010]; //leftP[i]表示i左侧p的个数,rightT[i]表示i右侧T的个数 int main(){    gets(str);    int n = strlen(str);    //填充leftP     for(int i = 0; i <= n; i++){        if(i > 0){ //不是首位             leftP[i] = leftP[i-1]; //继承上一位的结果         }        if(str[i] == 'P'){ //这一位是'P'             leftP[i]++; //leftP[i]++         }    }     int sum = 0;    int rightT = 0;    for(int i = n-1; i >= 0; i--){        if(str[i] == 'T'){            rightT++;        }else if(str[i] == 'A'){            sum = (sum + (rightT*leftP[i]))%1000000007;        }    }     printf("%d\n",sum);    return 0;}
0 0