牛客ACM-被3整除的子序列

题目描述

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

输入描述:

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

输出描述:

输出一个整数
示例1

输入

132

输出

3
示例2

输入

9

输出

1
示例3

输入

333

输出

7

这个是属于动态规划类型的题目,首先就会想到把大的问题分解为小问题来解决,把数字串从第一位逐步向最后一位进行求解任务,

1-为了方便处理,可以再每次循环的时候把数字串都进行模3处理  m = (s[i]-'0')%3; m暂时存放这一位的模3余数

2-需要确定状态转移方程,用dp[i][j]来表示在数字串的长度为i时,此时数字串模3余数为j的个数,
  当前位数的模3余数有三种情况(m=0或1或2):
  此时dp[i][j]应该为长度为i-1时的dp[i-1][j]加上dp[i-1][(j-m+3)%3]
  加入当前位的模3余数为1:我们在求dp[i][0]时肯定是加上dp[i-1][0]然后还要加上dp[i-1][?](前i-1长度的子序列模3余数为2的个数),此时的(?+m)%3=0
  也就是说?应该为2 我们的状态转移此时就是找到求?的规律:?=(j-m+3)%3;
  下面是我列出的列子:从中我们可以退出状态转移方程为:dp[i][j] = dp[i-1][j]+dp[i-1][(j-m+3)%3];
  (1)m=0:
    
  dp[i][0]=dp[i-1][0]+dp[i-1][0]
      dp[i][1]=dp[i-1][1]+dp[i-1][1]
      dp[i][2]=dp[i-1][2]+dp[i-1][2]
  (2)m=1:
      dp[i][0]=dp[i-1][0]+dp[i-1][2]
      dp[i][1]=dp[i-1][1]+dp[i-1][0]
      dp[i][1]=dp[i-1][1]+dp[i-1][0]
  (3)m=2:
      dp[i][0]=dp[i-1][0]+dp[i-1][1]
             dp [i][1]=dp[i-1][1]+dp[i-1][2]  
       dp[i][2]=dp[i-1][2]+dp[i-1][0]
 
   最后加上第i位的模3情况:
    代码:
      
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
int main(){
    int dp[55][3];
    int m=0;
    string str;
    cin>>str;
    //获取长度
    int strLength = str.length();
    memset( dp, 0, sizeof(dp)) ;//初始化数组
    dp[0][(str[0]-'0')%3] = 1;
     for(int i=1; i < strLength; i++){
         m = (str[i]-'0')%3;
         for(int j=0; j<3; j++){
             dp[i][j] = (dp[i-1][j] + dp[i-1][(j-m+3)%3])%mod;
         }
         dp[i][m] = (dp[i][m] + 1)%mod;//最后加上第i位的模3情况:
     }
     cout<<dp[strLength-1][0]<<endl; 
}
 
         
 

猜你喜欢

转载自www.cnblogs.com/Tisou1/p/12224095.html