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

题目链接在此

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


来了,今天用《算法笔记》的方法实现了一遍,便于理解和总结“打表+递推将时间复杂度减少到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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值