题目描述
你喜欢3070显卡吗?不,这不重要,看下面。
给你一个长度为50的数字串,问你有多少个子序列构成的数字可以被3整除
答案对1e9+7取模,子序列不一定连续
输入描述
输入一个字符串,由数字构成,长度小于等于50
输出描述
输出一个整数
输入样例 1
132
输出样例 1
3
输入样例 2
9
输出样例 2
1
输入样例 3
333
输出样例 3
7
输入样例 4
123456
输出样例 4
23
输入样例 5
00
输出样例 5
3
提示
n为长度 子任务1: n <= 5 子任务2: n <= 20 子任务3: 无限制 用动态规划写,可以找规律试一试
解题思路
看完题目,想法就是暴力搜索,把它的每一个子序列都判断一下,但是数字串长度最长为50,选取它的每一个子序列就是C(n,1)+...+C(n,n)=2^n-1(n为50)种可能;超时不行。
既然不行我们可以观察一下数字串之间是不是有什么特殊的连接(关系);
举一个例子,数字串123的所有子序列为1,2,3,12,13,23,123;我们对此进行判断发现
所有位数相加%3为0的是3,12,123;3个
为1的是1,13;2个
为2的是2,23;2个
那么数字串123有3个满足条件的子序列
现在我们在后面加4变成数字串1234,它的所有子序列为1,2,3,12,13,23,123,14,24,34,,124,134,234,1234,我们将其和数字串123的子序列对比可以发现,1234的子序列在包含123的子序列的基础上,增加了在原来子序列后面加4和4的子序列,这意味着我们判断1234有多少个满足条件的子序列可以根据123的情况推出,如下
原来的子序列判断过了,不需要重新判断,
4%3为1,则增加的新子序列满足条件的是由原来%3为2得到的
1234的子序列中满足条件的个数为 原来满足条件的子序列+原来%3为2的子序列
以此类推,我们可以对数字串从头到尾遍历,由前面的情况和新加数来推出现在的情况,则就需要一个数组dp[i][j]来存储不同长度的情况(dp[i][j]表示数字串前i位的所有子序列中%3为j的个数,j为0,1,2)
状态转移方程为
dp[i][j]=dp[i-1][j]+dp[i-1][x]((x+(新加数%3))%3=j,特殊情况:当j=新加数%3时,需要加1,因为新加数可以单独为子序列)
题解代码
简写形式
#include <bits/stdc++.h>
using namespace std;
int main()
{
int N = 1e9 + 7;
string str;
int len;
cin>>str;
len=str.length();
int arr[len+5][3];///存储前j位字符串子串除3余数为i的个数
memset(arr,0,sizeof(arr));
int x=(str[0]-'0')%3;
arr[0][x]++;
for(int i=1;i<len;i++){
x=(str[i]-'0')%3;
arr[i][(0+x)%3]=arr[i-1][0]+arr[i-1][(0+x)%3];
arr[i][(1+x)%3]=arr[i-1][1]+arr[i-1][(1+x)%3];
arr[i][(2+x)%3]=arr[i-1][2]+arr[i-1][(2+x)%3];
arr[i][x]++;
arr[i][0]=arr[i][0]%N;
arr[i][1]=arr[i][1]%N;
arr[i][2]=arr[i][2]%N;
}
//cout<<arr[0][0]<<" "<<arr[0][1]<<" "<<arr[0][2]<<endl;
cout<<arr[len-1][0]<<endl;
}
分情况讨论
#include<bits/stdc++.h>
#define N 1000000007
using namespace std;
int dp[50][3];
int main()
{
string s;
cin>>s;
int m=(s[0]-'0')%3;
dp[0][m]=1;
for(int i=1;i<s.size();i++)
{
m=(s[i]-'0')%3;
switch(m)
{
case 0:
{
dp[i][0]=(dp[i-1][0]*2+1)%N;
dp[i][1]=(dp[i-1][1]*2)%N;
dp[i][2]=(dp[i-1][2]*2)%N;
break;
}
case 1:
{
dp[i][0]=(dp[i-1][0]+dp[i-1][2])%N;
dp[i][1]=(dp[i-1][1]+dp[i-1][0]+1)%N;
dp[i][2]=(dp[i-1][2]+dp[i-1][1])%N;
break;
}
case 2:
{
dp[i][0]=(dp[i-1][0]+dp[i-1][1])%N;
dp[i][1]=(dp[i-1][1]+dp[i-1][2])%N;
dp[i][2]=(dp[i-1][2]+dp[i-1][0]+1)%N;
break;
}
}
}
cout<<dp[s.size()-1][0];
}