本文涉及知识点
LeetCode3757. 有效子序列的数量
数组的 强度 定义为数组中所有元素的 按位或 (Bitwise OR) 。
如果移除某个 子序列 会使剩余数组的 强度严格减少 ,那么该子序列被称为 有效子序列 。
返回数组中 有效子序列 的数量。由于答案可能很大,请返回结果对 109 + 7 取模后的值。
子序列 是一个 非空 数组,它是由另一个数组删除一些(或不删除任何)元素,并且不改变剩余元素的相对顺序得到的。
空数组的按位或为 0。
示例 1:
输入: nums = 1,2,3
输出: 3
解释:
数组的按位或为 1 OR 2 OR 3 = 3。
有效子序列为:
1, 3:剩余元素 2 的按位或为 2。
2, 3:剩余元素 1 的按位或为 1。
1, 2, 3:剩余元素 \[\] 的按位或为 0。
因此,有效子序列的总数为 3。
示例 2:
输入: nums = 7,4,6
输出: 4
解释:
数组的按位或为 7 OR 4 OR 6 = 7。
有效子序列为:
7:剩余元素 4, 6 的按位或为 6。
7, 4:剩余元素 6 的按位或为 6。
7, 6:剩余元素 4 的按位或为 4。
7, 4, 6:剩余元素 \[\] 的按位或为 0。
因此,有效子序列的总数为 4。
示例 3:
输入: nums = 8,8
输出: 1
解释:
数组的按位或为 8 OR 8 = 8。
只有子序列 8, 8 是有效的,因为移除它会使剩余数组为空,按位或为 0。
因此,有效子序列的总数为 1。
示例 4:
输入: nums = 2,2,1
输出: 5
解释:
数组的按位或为 2 OR 2 OR 1 = 3。
有效子序列为:
1:剩余元素 2, 2 的按位或为 2。
2, 1(包括 nums0 和 nums2):剩余元素 2 的按位或为 2。
2, 1(包括 nums1 和 nums2):剩余元素 2 的按位或为 2。
2, 2:剩余元素 1 的按位或为 1。
2, 2, 1:剩余元素 \[\] 的按位或为 0。
因此,有效子序列的总数为 5。
提示:
1 < = n u m s . l e n g t h < = 1 0 5 1 <= nums.length <= 10^5 1<=nums.length<=105
1 < = n u m s i < = 1 0 6 1 <= numsi <= 10^6 1<=numsi<=106
SOSDP(高维前缀和DP、子集和DP)
dp0i = n u m s . c o u n t ( i ) nums.count(i) nums.count(i)
dp11=dp01+dp00 : 1的数量
dp13=dp03+dp02 3和2的数量
dp15=dp05+dp04 5和4的数量
dp17=dp07+dp07 6和7的数量
dp23=dp13+dp11 1,2,3的数量
dp27=dp17+dp15 4,5,6,7的数量
dp37=dp27+dp23 1到7的数量
dpbitmask记录符合以下三个条件的x的数量:
一,mask任意二进制位为0,则x的此二进制位位0。
二,mask的第i个二进制位为1,最低位是第0位。i<bit,x的第x位可以为0或1;否则必须为1。
cpp
for (int bit = 0; bit < M; bit++) {
for (int m = 0; m < MC; m++) {
if (m & (1 << bit)) {
dp[bit+1][m] = dp[bit ][m] + dp[bit ][m ^ (1 << bit)];
}
else {
dp[bit + 1][m] = dp[bit][m];
}
}
}
时间复杂度 :O(NlogN)
dpbit m \^ (1 \<\< bit) 和dpbit -1m \^ (1 \<\< bit) 相同。
dpbit+1m 只被读取修改一次,且是同一次修改。
故可以降维,忽略bit,只保留m。
cpp
for (int bit = 0; bit < M; bit++) {
for (int m = 0; m < MC; m++) {
if (m & (1 << bit)) {
dp[m] = dp[m] + dp[m ^ (1 << bit)];
}
}
}
代码
核心代码
cpp
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;
};
typedef C1097Int<> BI;
class Solution {
public:
int countEffective(vector<int>& nums) {
const int N = nums.size();
int iMax = 0;
for (const auto& n : nums) {
iMax |= n;
}
int M = 0, MC;
for (; (1 << M)<= iMax; M++);
MC = 1 << M;
vector<vector<int>> dp(M+1, vector<int>(MC));
for (const auto& n : nums) {
dp[0][n]++;
}
for (int bit = 0; bit < M; bit++) {
for (int m = 0; m < MC; m++) {
if (m & (1 << bit)) {
dp[bit+1][m] = dp[bit ][m] + dp[bit ][m ^ (1 << bit)];
}
else {
dp[bit + 1][m] = dp[bit][m];
}
}
}
CBitCounts bc(MC);
C1097Int<> ans;
for (int m = 0; m <= iMax; m++) {
if ((m | iMax) != iMax) { continue; }
BI cur = BI(2).pow(dp.back()[m]) - 1;
if(1& (bc.m_vCnt[m] + bc.m_vCnt[iMax])){
ans -= cur;
}
else {
ans += cur;
}
}
return (BI(2).pow(N)-ans).ToInt();
}
};
第二版代码
cpp
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;
};
typedef C1097Int<> BI;
class Solution {
public:
int countEffective(vector<int>& nums) {
const int N = nums.size();
int iMax = 0;
for (const auto& n : nums) {
iMax |= n;
}
int M = 0, MC;
for (; (1 << M)<= iMax; M++);
MC = 1 << M;
vector<int> dp(MC);
for (const auto& n : nums) {
dp[n]++;
}
for (int bit = 0; bit < M; bit++) {
for (int m = 0; m < MC; m++) {
if (m & (1 << bit)) {
dp[m] = dp[m] + dp[m ^ (1 << bit)];
}
}
}
CBitCounts bc(MC);
C1097Int<> ans;
for (int m = 0; m <= iMax; m++) {
if ((m | iMax) != iMax) { continue; }
BI cur = BI(2).pow(dp[m]) - 1;
if(1& (bc.m_vCnt[m] + bc.m_vCnt[iMax])){
ans -= cur;
}
else {
ans += cur;
}
}
return (BI(2).pow(N)-ans).ToInt();
}
};
单元测试
cpp
vector<int> nums;
vector<vector<int>> queries;
TEST_METHOD(TestMethod001)
{
nums = { 1};
auto res = Solution().countEffective(nums);
AssertEx(1, res);
}
TEST_METHOD(TestMethod11)
{
nums = { 1,2,3 };
auto res = Solution().countEffective(nums);
AssertEx(3, res);
}
TEST_METHOD(TestMethod12)
{
nums = { 7,4,6 };
auto res = Solution().countEffective(nums);
AssertEx(4, res);
}
TEST_METHOD(TestMethod13)
{
nums = { 8,8 };
auto res = Solution().countEffective(nums);
AssertEx(1, res);
}
TEST_METHOD(TestMethod14)
{
nums = { 2,2,1 };
auto res = Solution().countEffective(nums);
AssertEx(5, res);
}
TEST_METHOD(TestMethod15)
{
nums = { 8,12 };
auto res = Solution().countEffective(nums);
AssertEx(2, res);
}

扩展阅读
| 我想对大家说的话 |
|---|
| 工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
| 学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
| 有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
| 员工说:技术至上,老板不信;投资人的代表说:技术至上,老板会信。 |
| 闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
| 子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
| 如果程序是一条龙,那算法就是他的是睛 |
| 失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步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++**实现。
