本文涉及知识点
逆向思考
3352. 统计小于 N 的 K 可约简整数
给你一个 二进制 字符串 s,它表示数字 n 的二进制形式。
同时,另给你一个整数 k。
如果整数 x 可以通过最多 k 次下述操作约简到 1 ,则将整数 x 称为 k-可约简 整数:
将 x 替换为其二进制表示中的置位数(即值为 1 的位)。
例如,数字 6 的二进制表示是 “110”。一次操作后,它变为 2(因为 “110” 中有两个置位)。再对 2(二进制为 “10”)进行操作后,它变为 1(因为 “10” 中有一个置位)。
返回小于 n 的正整数中有多少个是 k-可约简 整数。
由于答案可能很大,返回结果需要对
10
9
10^9
109 + 7 取余。
二进制中的置位是指二进制表示中值为 1 的位。
示例 1:
输入: s = “111”, k = 1
输出: 3
解释:
n = 7。小于 7 的 1-可约简整数有 1,2 和 4。
示例 2:
输入: s = “1000”, k = 2
输出: 6
解释:
n = 8。小于 8 的 2-可约简整数有 1,2,3,4,5 和 6。
示例 3:
输入: s = “1”, k = 3
输出: 0
解释:
小于 n = 1 的正整数不存在,因此答案为 0。
提示:
1 <= s.length <= 800
s 中没有前导零。
s 仅由字符 ‘0’ 和 ‘1’ 组成。
1 <= k <= 5
逆序思考
1逆序转换一次,可以是任意2的幂,特征是:二进制形式有且只有一个1。
0
≤
x
≤
800
0 \le x \le 800
0≤x≤800,如果通过之多k-1,能逆向转化为x,则:k次能转成 任意二进位刚好x个1的数。
如果不能通过之多k-1次逆序转化成x;则k次一定不能转化成二进制刚好x个1的数。
x小于s有特征一,一定存在i:
x[i] < s[i],且
x
[
0
⋯
i
−
1
]
=
=
x
[
0
⋯
i
−
1
]
x[0\cdots i-1]==x[0\cdots i-1]
x[0⋯i−1]==x[0⋯i−1],余下的位相同。
实现
N=s.length
初始:pre[1]=true;
迭代k-1次,每次:
cur = pre;
通过i枚举0到N{
dp[i] |= dp[i中1的个数]。
}
第一层循环:通过i枚举0到N,表示二进制位1的数量为i,忽略dp[i]为假。
第二层循环:枚举特征一。
前面的1大于i,忽略;余下的空位小于1,忽略。
代码
核心代码
template<long long MOD = 1000000007,class T1 = int, class T2 = long long>
class C1097Int
{
public:
C1097Int(T1 iData = 0) :m_iData(iData% MOD)
{
}
C1097Int(T2 llData) :m_iData(llData% MOD) {
}
C1097Int operator+(const C1097Int& o)const
{
return C1097Int(((T2)m_iData + o.m_iData) % MOD);
}
C1097Int& operator+=(const C1097Int& o)
{
m_iData = ((T2)m_iData + o.m_iData) % MOD;
return *this;
}
C1097Int& operator-=(const C1097Int& o)
{
m_iData = ((T2)MOD + m_iData - o.m_iData) % MOD;
return *this;
}
C1097Int operator-(const C1097Int& o)const
{
return C1097Int(((T2)MOD + m_iData - o.m_iData) % MOD);
}
C1097Int operator*(const C1097Int& o)const
{
return((T2)m_iData * o.m_iData) % MOD;
}
C1097Int& operator*=(const C1097Int& o)
{
m_iData = ((T2)m_iData * o.m_iData) % MOD;
return *this;
}
C1097Int operator/(const C1097Int& o)const
{
return *this * o.PowNegative1();
}
C1097Int& operator/=(const C1097Int& o)
{
*this *= o.PowNegative1();
return *this;
}
bool operator==(const C1097Int& o)const
{
return m_iData == o.m_iData;
}
bool operator<(const C1097Int& o)const
{
return m_iData < o.m_iData;
}
C1097Int pow(T2 n)const
{
C1097Int iRet = (T1)1, iCur = *this;
while (n)
{
if (n & 1)
{
iRet *= iCur;
}
iCur *= iCur;
n >>= 1;
}
return iRet;
}
C1097Int PowNegative1()const
{
return pow(MOD - 2);
}
T1 ToInt()const
{
return ((T2)m_iData + MOD) % MOD;
}
private:
T1 m_iData = 0;;
};
class CBitCounts
{
public:
CBitCounts(int iMaskCount)
{
m_vCnt.resize(iMaskCount);
for (int i = 1; i < iMaskCount; i++)
{
m_vCnt[i] = 1 + m_vCnt[i & (i - 1)];
}
}
template<class T>
static int bitcount(T x) {
int countx = 0;
while (x) {
countx++;
x &= (x - 1);
}
return countx;
}
vector<int> m_vCnt;
};
template<class Result = C1097Int<> >
class CCombination
{
public:
CCombination()
{
m_v.assign(1, vector<Result>(1,1));
}
Result Get(int sel, int total)
{
assert(sel <= total);
while (m_v.size() <= total)
{
int iSize = m_v.size();
m_v.emplace_back(iSize + 1, 1);
for (int i = 1; i < iSize; i++)
{
m_v[iSize][i] = m_v[iSize - 1][i] + m_v[iSize - 1][i - 1];
}
}
return m_v[total][sel];
}
protected:
vector<vector<Result>> m_v;
};
class Solution {
public:
int countKReducibleNumbers(string s, int K) {
typedef C1097Int<> BI;
const int N = s.length();
vector<bool> pre(N+1);
pre[1] = true;
CBitCounts bc(N + 1);
for (int k = 1; k < K; k++) {
auto cur = pre;
for (int i = 0; i <= N; i++) {
cur[i] = cur[i] || pre[bc.m_vCnt[i]];
}
pre.swap(cur);
}
BI ans = 0;
CCombination com;
for (int i = 0; i <= N; i++) {
if (!pre[i]) { continue; }
int preOne = 0;
for (int j = 0; j < N; j++) {
if ('0' == s[j]) { continue; }
if (i < preOne) { break; }
const int iReMain = i - preOne;
const int has = N - j - 1;
preOne++;
if ( iReMain > has ) { continue; }
ans += com.Get(iReMain, has);
}
}
return ans.ToInt();
}
};
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
员工说:技术至上,老板不信;投资人的代表说:技术至上,老板会信。 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。