题目描述
难度分:1547
输入一个长度至多为105+1的二进制字符串s,不含前导零。
把s当作一个二进制数。输出有多少对非负整数(a,b),满足a+b≤s且a+b=a⊕b。答案模109+7。
输入样例1
10
输出样例1
5
输入样例2
1111111111111111111
输出样例2
162261460
算法
动态规划
状态定义
f[i][1]表示a+b的二进制前i位恰好和s相同的方案数,f[i][0]表示严格小于s的方案数。两种情况算在一起就是a+b≤s的所有集合,在这个定义下答案就是f[n][0]+f[n][1],其中n是s的长度。
状态转移
考虑等于的情况:如果s[i]=1
,那么恰好等于s的情况下a和b在这一位可以填0,1或1,0,一共两种方案,状态转移方程为f[i][1]=f[i−1][1]×2。如果s[i]=0
,那么a和b在当前位就只能填0,0,只有一种方案,状态转移方程为f[i][0]=f[i−1][0]。
考虑小于的情况:如果前面已经保证小于s了,则当前位只要不进位怎么填都行,有0,1、1,0、0,0三种情况,状态转移方程为f[i][0]=f[i−1][0]×3。如果前面都是相等的(相当于数位DP
时前面的位满足islimit=true
),只有在s[i]=1
时才有这种情况,此时只要在当前位填0,0即可,状态转移方程为f[i][0]=f[i−1][0]×3+f[i−1][1]。
初始情况下考虑0位s=0,f[0][0]=0不存在严格小于的情况,存在一种等于的情况f[0][1]=1。
复杂度分析
时间复杂度
遍历一遍字符串s就可以求得答案,而单次转移是O(1)的,所以整个算法的时间复杂度为O(n)。
空间复杂度
DP
数组f是滚动实现的,因此除去输入的字符串s,仅使用了有限几个变量。整个算法的额外空间复杂度为O(1)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
string L;
int n;
int main() {
cin >> L;
n = L.size();
int f0 = 0, f1 = 1;
for(int i = 0; i < n; i++) {
f0 = f0 * 3LL % MOD;
if(L[i] == '1') {
f0 = (f0 + f1) % MOD;
f1 = f1 * 2LL % MOD;
}
}
cout << (f0 + f1) % MOD << endl;
return 0;
}