dp 集合思想优化

链接:https://ac.nowcoder.com/acm/contest/78807/D
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

Bingbong 有一个长度为 n 的数字字符串 S,该字符串仅包含 [0, 9] 的数字。

Bingbong 想要从中挑选出若干个字符,然后按照其相对顺序重新拼接而成一个数字,使其是一个无前导 0 的偶数

例如:当 n = 3, S = 100。其包含的偶数数字有 0, 0, 10, 10, 100。而 00 是不符合条件的,因为其含有前导 0。

由于字符串实在是太长了,他一个人数不过来,请您帮他计算一下该字符串中含有的偶数方案总数, 结果对 1e9 + 7 取模。

输入描述:

第一行一个整数 n (1 ≤ n ≤ 2×10^5),表示字符串 S 的长度。

第二行一个长度为 n 的字符串 S ,保证输入只含 [0, 9] 的数字。

输出描述:

一个整数,表示最后含有的偶数方案总数。

示例1

输入

3
100

输出

5

示例2

输入

5
12345

输出

10

解答

  • 此题使用 dp 的思想,记录之前所有的方案,然后看要加入的数能否构成答案,如果可以就加入答案,并计算这个数能构成的方案
  • ans 表示的是从它往前的偶数集合
  • pre 表示的是当前数前面的数,能构成哪些数的集合,这就不管偶数还是奇数了,表示一个集合
  • 很抽象,对吧
  • 假设 1024,遍历到 2 的时候,pre 表示的是:10,1,0,解释 ans = ans + pre + 1pre 要加是因为 pre 是一个集合,这个集合内的数加上当前数一定是偶数,ans 是前面合法情况, +1 是加上当前数单独作为一种情况
  • 解释为啥 s[i -1] != '0',要 pre++,原因是当前数的前者数是偶数,需要把这个数加入 pre,而加入后组成的数,前面 pre 可选,可不选,当前数可选可不选,后面也就是 (pre + 1) * 2 种方案也就满足,而等于 0 情况,也就是当前 0 一定不选,前面 pre 可选可不选,直接 pre * 2,所以不等于加 1,然后再 ×2,另一个直接 x2
#include <iostream>
#include <string>
using namespace std;
const int mod = 1e9 + 7;
int n;
string s;

int main()
{
    cin >> n >> s;

    long long ans = 0, pre = 0;
    for (int i = 0; i < n; i++) 
    {
        if (i > 0 && s[i - 1] != '0') pre++;
        if (s[i] % 2 == 0) ans = (ans + pre + 1) % mod;
        
        pre = (pre * 2) % mod;
    }

    cout << ans << endl;
    return 0;
}
posted @ 2024-04-25 19:22  星竹z  阅读(6)  评论(0编辑  收藏  举报