牛客练习题---21302: 被3整除的子序列

链接:https://ac.nowcoder.com/acm/problem/21302
来源:牛客网

题目:

给你一个长度为50的数字串,问你有多少个子序列构成的数字可以被3整除
答案对1e9+7取模

输入:

输入一个字符串,由数字构成,长度小于等于50

输出:

输出一个整数

 分析:

对一个数字字符串取余有:

123456789 % m =(((1 * 10 + 2) % m * 10 + 3) % m * 10+ 4) % m......

定义dp[pos][0/1][mod],表示第 pos 位 取/不取 时余数为 mod 时的子序列个数,mod属于【0,2】

有如下转移方程:

(1)不取pos位

        dp[pos][0][mod] = dp[pos-1][0][mod] + dp[pos-1][1][mod]

(2)取第pos位:

        dp[pos][1][mod] = dp[pos-1][0][k] + dp[pos-1][1][k]

        K应该满足:(K * 10 % 3 + a[pos])% 3 = mod,暴力K的范围[0,2],满足条件时就转移

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD = 1e9+7;
LL dp[60][2][3],a[60];
char s[60];
int main()
{
    scanf("%s",s+1);
    int len = strlen(s+1);
    for(int i = 1; i <= len; ++i)
    {
        a[i] = (s[i] - '0') % 3;
    }
    dp[0][0][0] = 1;                             //开始时都不选且余数为0 = 1
    for(int i = 1 ; i <= len; ++i)
    {
        for(int j = 0;j < 3; ++j)
        {
            dp[i][0][j] = (dp[i-1][0][j] + dp[i-1][1][j]) % MOD;
            for(int k = 0; k < 3 ; ++k)
            {
                if((k+a[i]) % 3 == j)
                    dp[i][1][j] = (dp[i-1][0][k]  + dp[i-1][1][k]) % MOD;
            }
        }
    }
    cout<<(dp[len][1][0] + dp[len][0][0] - 1 + MOD) % MOD;    //减去全不选的情况
    return 0;
}

      

猜你喜欢

转载自blog.csdn.net/qq_41157137/article/details/86555286