求本质不同的子序列数目
法一
\(dp_{i}\) 表示以 \(i\) 为结尾的答案,记录 \(las_i\) 表示 \(i\) 上一次出现的位置。
\(dp_i = \sum_{j = las_{a_i}}^{i-1} dp_j\),可以前缀和优化。
最后答案为 \(sum_n -1\),因为这里的答案包含空串,要减去。
点击查看代码
// 德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱
// 德丽莎的可爱在于德丽莎很可爱,德丽莎为什么很可爱呢,这是因为德丽莎很可爱!
// 没有力量的理想是戏言,没有理想的力量是空虚
#include <bits/stdc++.h>
#define LL long long
using namespace std;
char ibuf[1 << 15], *p1, *p2;
#define getchar() (p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 1 << 15, stdin), p1==p2) ? EOF : *p1++)
inline int read() {
char ch = getchar(); int x = 0, f = 1;
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
return x * f;
}
void print(LL x) {
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
template<class T> bool chkmin(T &a, T b) { return a > b ? (a = b, true) : false; }
template<class T> bool chkmax(T &a, T b) { return a < b ? (a = b, true) : false; }
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define repd(i, l, r) for (int i = (l); i >= (r); i--)
#define REP(i, l, r) for (int i = (l); i < (r); i++)
const int N = 2e6, mod = 1e9 + 7;
int n, a[N], dp[N], sum[N], las[N];
void solve() {
n = read();
rep (i, 1, n) a[i] = read();
sum[0] = 1;
rep (i, 1, n) {
if (las[ a[i] ] == 0) dp[i] = sum[i - 1];
else dp[i] = sum[i - 1], dp[i] -= sum[ las[ a[i] ] - 1 ], dp[i] %= mod;
sum[i] = sum[i - 1] + dp[i]; sum[i] %= mod;
las[ a[i] ] = i;
}
int ans = sum[n] - 1; ans %= mod; ans += mod; ans %= mod;
cout << ans << "\n"; return;
}
signed main () {
#ifdef LOCAL_DEFINE
freopen("1.in", "r", stdin);
freopen("1.ans", "w", stdout);
#endif
int T = 1; while (T--) solve();
#ifdef LOCAL_DEFINE
cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
return 0;
}
法二
\(dp_{i}\) 表示以 \(i\) 为结尾的答案,记录 \(las_i\) 表示 \(i\) 上一次出现的位置。
如果当前 \(a_i\) 还没有出现过,那么 \(dp_i = 2 \times dp_{i - 1} + 1\)。
表示前面的 \(dp_{i-1}\) 中是否加入一个 \(a_i\),以及只选 \(a_i\) 的情况 (\(+1\))。
如果当前 \(a_i\) 已经出现过了,其中对于 \([1,las_{a_i}−1]\) 中的状态将会重复计算,那么需要减去这部分的答案,于是 \(dp_i = 2 \times dp_{i - 1} - dp_{las_{a_i} - 1}\)。
点击查看代码
// 德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱
// 德丽莎的可爱在于德丽莎很可爱,德丽莎为什么很可爱呢,这是因为德丽莎很可爱!
// 没有力量的理想是戏言,没有理想的力量是空虚
#include <bits/stdc++.h>
#define LL long long
using namespace std;
char ibuf[1 << 15], *p1, *p2;
#define getchar() (p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 1 << 15, stdin), p1==p2) ? EOF : *p1++)
inline int read() {
char ch = getchar(); int x = 0, f = 1;
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
return x * f;
}
void print(LL x) {
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
template<class T> bool chkmin(T &a, T b) { return a > b ? (a = b, true) : false; }
template<class T> bool chkmax(T &a, T b) { return a < b ? (a = b, true) : false; }
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define repd(i, l, r) for (int i = (l); i >= (r); i--)
#define REP(i, l, r) for (int i = (l); i < (r); i++)
const int N = 2e6, mod = 1e9 + 7;
int n, a[N], dp[N], las[N];
void solve() {
n = read();
rep (i, 1, n) a[i] = read();
dp[1] = 1; las[ a[1] ] = 1;
rep (i, 2, n) {
if (las[ a[i] ] == 0) dp[i] = dp[i - 1] * 2 + 1, dp[i] %= mod;
else {
dp[i] = dp[i - 1] * 2 - dp[ las[ a[i] ] - 1 ];
dp[i] %= mod; dp[i] += mod; dp[i] %= mod;
}
las[ a[i] ] = i;
}
dp[n] %= mod; dp[n] += mod; dp[n] %= mod;
cout << dp[n] << "\n";
}
signed main () {
#ifdef LOCAL_DEFINE
freopen("1.in", "r", stdin);
freopen("1.ans", "w", stdout);
#endif
int T = 1; while (T--) solve();
#ifdef LOCAL_DEFINE
cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
return 0;
}
法三
设 \(f_{i,j}\) 表示前 \(i\) 个字符,选 \(j\) 的答案。
\[f_{i,j}=
\begin{cases}
f_{i-1,j},s_i \neq j\\
\sum\limits_k f_{i-1,k}+1,s_i=j\\
\end{cases}
\]
可以矩阵优化,对角线染 \(1\),然后对应列染 \(1\) 即可。
法四
设 \(f_{i,j,k}\) 表示前 \(i\) 个字符,以 \(j\) 结尾,长度为 \(k\) 的答案。
\[f_{i,j,k}=
\begin{cases}
f_{i-1,j,k},s_i \neq j\\
\sum\limits_t f_{i-1,t,k-1}+[k=1],s_i=j\\
\end{cases}
\]