题意
题解
DP
令以 b [ i ] b[i] b[i]为首元素的子序列集合为 S i \mathcal{S}_{i} Si。若 b [ i ] = b [ j ] b[i]=b[j] b[i]=b[j],且 i < j i<j i<j,则 S j ⊆ S i \mathcal{S}_{j}\subseteq\mathcal{S}_{i} Sj⊆Si。令 d p [ i ] = ∣ S i ∣ dp[i]=\lvert\mathcal{S}_{i}\rvert dp[i]=∣Si∣,则枚举子序列的第二个元素 x x x进行转移,转移的位置是 b [ j ] = x , i < j b[j]=x,i<j b[j]=x,i<j的最小 j j j。总时间复杂度 O ( n ) O(n) O(n)。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int numberOfUniqueGoodSubsequences(string binary) {
int n = binary.size();
vector<vector<int>> nxt(n, vector<int>(2));
vector<int> lst(2, -1);
for (int i = n - 1; i >= 0; --i) {
nxt[i] = lst;
lst[binary[i] - '0'] = i;
}
vector<int> dp(n, -1);
const int mod = 1e9 + 7;
auto add = [&](int &x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
};
for (int i = n - 1; i >= 0; --i) {
dp[i] = 1;
for (int j = 0; j < 2; ++j) {
if (nxt[i][j] != -1) {
add(dp[i], dp[nxt[i][j]]);
}
}
}
int res = 0;
if (lst[0] != -1) {
res = 1;
}
if(lst[1] != -1) add(res, dp[lst[1]]);
return res;
}
};
Trie
用Trie维护子序列集合,树中每一个节点代表一个互异的子序列。维护Trie中子树数量小于2的节点数量,不断插入新元素,同时将新增的树节点计入答案。总时间复杂度 O ( n ) O(n) O(n)。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int numberOfUniqueGoodSubsequences(string binary) {
const int mod = 1e9 + 7;
auto add = [&](int &x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
};
int zero = 0;
for (auto c : binary) {
if (c == '0') {
zero = 1;
}
}
int p = 0;
int n = binary.size();
while (p < n && binary[p] == '0') {
p += 1;
}
int res = zero;
if (p < n) {
vector<int> a{1, 0, 0};
res += 1;
p += 1;
for (int i = p; i < n; ++i) {
int x = binary[i] - '0';
vector<int> tmp(3);
add(tmp[0], a[0]);
add(tmp[0], a[1 << (x ^ 1)]);
add(res, tmp[0]);
add(tmp[1 << x], a[0]);
add(tmp[1 << x], a[1 << x]);
swap(tmp, a);
}
}
return res;
}
};