传智杯初赛(二)

题目二:
dp(动态规划经典)
leafee 最近爱上了 abb 型语句,比如“叠词词”、“恶心心”

leafee 拿到了一个只含有小写字母的字符串,她想知道有多少个 "abb" 型的子序列?
定义: abb 型字符串满足以下条件:

  1. 字符串长度为 3 。
  2. 字符串后两位相同。
  3. 字符串前两位不同。

    #include<bits/stdc++.h>
using namespace std;
long long sum[101010][26];
int main(){
    int n,i,j;
    string s;
    cin>>n>>s;
    for(i=n-1;i>=0;i--){
        for(j=0;j<26;j++)sum[i][j]=sum[i+1][j];
        sum[i][s[i]-'a']++;
    }
    long long res=0;
    for(i=0;i<n;i++){
        for(j=0;j<26;j++){
            if(j!=s[i]-'a'){
                res+=sum[i+1][j]*(sum[i+1][j]-1)/2;
            }
        }
    }
    cout<<res;
}

补充:

DP:将一个大的问题,不断拆分为一个个小的问题,小问题和小问题之间是存在联系的。一般我们都可以通过递推关系来去找到它们之间的联系。

它可能是由前面的某些数的组合得到,也有可能是由前面的某些数求得最优解得到,等等。这个时候,其就会和贪心、排列组合等知识组合到了一起。而如何找到这样一个作用方式,正是dp问题的关键。

一般而言,每一种独立的结果我们称之为状态。而我们将作用方式称之为状态转移方程。

理解:

1、拆分子问题;将大问题拆分成小问题。

2、记录状态;每一个子问题得到的状态一般都记录在了一个dp数组当中,或者是通过栈、队列等其他的方式记录。

3、DP无后效性。按照一定顺序去求解拆分的小问题。当小问题的状态或者结果得到了之后,我们就再也不去关心它是怎么样得到的了。

方法:

1、首先我们将一个大的、整体的问题拆开,拆成小的问题。那么我们就要确定,每一个小的问题解决之后所得到的结果表示的是什么。即确定每一个小的问题表示的状态。比如,dp[i]表示前i个数的某种方法的个数,再比如,dp[i][j]表示某个字符串从i到j是不是回文串等等。就是说,你要明白,你的每一张多米诺骨牌代表的是什么东西。

2、确立每一个状态之间的转移关系。多米诺骨牌立好之后,是不是要想着这些骨牌要怎么摆放的问题?那么状态转移就是可以理解为用某种方法,将骨牌摆好。比如,dp[i] = dp[i-1]+dp[i-2],复杂一点的,可能还有类似于dp[i] = max(dp[i],dp[i-dp[j]]) (j = 0, 1, 2...,i - dp[j] >= 0)这样。

3、确立初始状态。就是给多米诺骨牌一个初始的动力,让它可以触发效应,一个一个全部倒下。最最常见的,就比如dp[0] = 1 这样子的。也叫确立边界条件。

指在求解问题当中的遍历顺序。这点和上述的是紧密相关的。也就是说,你要明白你的骨牌是怎样一个个倒下的(是从后往前?还是从前往后?)最典型的,当进行空间优化的时候,就会看出遍历顺序所带来的重要作用。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值