|
48 | 48 |
|
49 | 49 | <!-- 这里可写通用的实现逻辑 -->
|
50 | 50 |
|
| 51 | +**方法一:枚举 + 组合计数** |
| 52 | + |
| 53 | +由于题目说的是子序列中字母出现的次数,因此,我们可以先用一个数组 `cnt` 统计字符串 $s$ 中每个字母出现的次数,记最大的次数为 $mx$。 |
| 54 | + |
| 55 | +接下来,我们在 $[1,2..mx]$ 范围内枚举子序列中字母出现的次数 $i$,然后枚举所有出现过的字母,如果该字母 $c$ 的出现次数 $cnt[c]$ 大于等于 $i$,那么我们可以从这 $cnt[c]$ 个相同字母中选择其中 $i$ 个,也可以一个都不选,那么当前字母的可选方案数就是 $comb(cnt[c], i) + 1$,将所有可选方案数相乘,可以得到当前次数的所有子序列次数,将次数减去 $1$ 累加到答案中。 |
| 56 | + |
| 57 | +那么问题的关键在于如何快速求出 $comb(n, k)$,我们可以用逆元来求解,具体实现见代码。 |
| 58 | + |
| 59 | +时间复杂度 $O(n \times C)$,空间复杂度 $O(n)$。其中 $n$ 为字符串 $s$ 的长度,而 $C$ 是字符集的大小,本题中 $C = 26$。 |
| 60 | + |
51 | 61 | <!-- tabs:start -->
|
52 | 62 |
|
53 | 63 | ### **Python3**
|
54 | 64 |
|
55 | 65 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
56 | 66 |
|
57 | 67 | ```python
|
58 |
| - |
| 68 | +N = 10001 |
| 69 | +MOD = 10**9 + 7 |
| 70 | +f = [1] * N |
| 71 | +g = [1] * N |
| 72 | +for i in range(1, N): |
| 73 | + f[i] = f[i - 1] * i % MOD |
| 74 | + g[i] = pow(f[i], MOD - 2, MOD) |
| 75 | + |
| 76 | + |
| 77 | +def comb(n, k): |
| 78 | + return f[n] * g[k] * g[n - k] % MOD |
| 79 | + |
| 80 | + |
| 81 | +class Solution: |
| 82 | + def countGoodSubsequences(self, s: str) -> int: |
| 83 | + cnt = Counter(s) |
| 84 | + ans = 0 |
| 85 | + for i in range(1, max(cnt.values()) + 1): |
| 86 | + x = 1 |
| 87 | + for v in cnt.values(): |
| 88 | + if v >= i: |
| 89 | + x = x * (comb(v, i) + 1) % MOD |
| 90 | + ans = (ans + x - 1) % MOD |
| 91 | + return ans |
59 | 92 | ```
|
60 | 93 |
|
61 | 94 | ### **Java**
|
62 | 95 |
|
63 | 96 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
64 | 97 |
|
65 | 98 | ```java
|
66 |
| - |
| 99 | +class Solution { |
| 100 | + private static final int N = 10001; |
| 101 | + private static final int MOD = (int) 1e9 + 7; |
| 102 | + private static final long[] F = new long[N]; |
| 103 | + private static final long[] G = new long[N]; |
| 104 | + |
| 105 | + static { |
| 106 | + F[0] = 1; |
| 107 | + G[0] = 1; |
| 108 | + for (int i = 1; i < N; ++i) { |
| 109 | + F[i] = F[i - 1] * i % MOD; |
| 110 | + G[i] = qmi(F[i], MOD - 2, MOD); |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + public static long qmi(long a, long k, long p) { |
| 115 | + long res = 1; |
| 116 | + while (k != 0) { |
| 117 | + if ((k & 1) == 1) { |
| 118 | + res = res * a % p; |
| 119 | + } |
| 120 | + k >>= 1; |
| 121 | + a = a * a % p; |
| 122 | + } |
| 123 | + return res; |
| 124 | + } |
| 125 | + |
| 126 | + public static long comb(int n, int k) { |
| 127 | + return (F[n] * G[k] % MOD) * G[n - k] % MOD; |
| 128 | + } |
| 129 | + |
| 130 | + public int countGoodSubsequences(String s) { |
| 131 | + int[] cnt = new int[26]; |
| 132 | + int mx = 1; |
| 133 | + for (int i = 0; i < s.length(); ++i) { |
| 134 | + mx = Math.max(mx, ++cnt[s.charAt(i) - 'a']); |
| 135 | + } |
| 136 | + long ans = 0; |
| 137 | + for (int i = 1; i <= mx; ++i) { |
| 138 | + long x = 1; |
| 139 | + for (int j = 0; j < 26; ++j) { |
| 140 | + if (cnt[j] >= i) { |
| 141 | + x = x * (comb(cnt[j], i) + 1) % MOD; |
| 142 | + } |
| 143 | + } |
| 144 | + ans = (ans + x - 1) % MOD; |
| 145 | + } |
| 146 | + return (int) ans; |
| 147 | + } |
| 148 | +} |
67 | 149 | ```
|
68 | 150 |
|
69 | 151 | ### **C++**
|
70 | 152 |
|
71 | 153 | ```cpp
|
72 |
| - |
| 154 | +int N = 10001; |
| 155 | +int MOD = 1e9 + 7; |
| 156 | +long f[10001]; |
| 157 | +long g[10001]; |
| 158 | + |
| 159 | +long qmi(long a, long k, long p) { |
| 160 | + long res = 1; |
| 161 | + while (k != 0) { |
| 162 | + if ((k & 1) == 1) { |
| 163 | + res = res * a % p; |
| 164 | + } |
| 165 | + k >>= 1; |
| 166 | + a = a * a % p; |
| 167 | + } |
| 168 | + return res; |
| 169 | +} |
| 170 | + |
| 171 | +void init() { |
| 172 | + f[0] = 1; |
| 173 | + g[0] = 1; |
| 174 | + for (int i = 1; i < N; ++i) { |
| 175 | + f[i] = f[i - 1] * i % MOD; |
| 176 | + g[i] = qmi(f[i], MOD - 2, MOD); |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +int comb(int n, int k) { |
| 181 | + return (f[n] * g[k] % MOD) * g[n - k] % MOD; |
| 182 | +} |
| 183 | + |
| 184 | + |
| 185 | +class Solution { |
| 186 | +public: |
| 187 | + Solution() { |
| 188 | + init(); |
| 189 | + } |
| 190 | + int countGoodSubsequences(string s) { |
| 191 | + int cnt[26]{}; |
| 192 | + int mx = 1; |
| 193 | + for (char& c : s) { |
| 194 | + mx = max(mx, ++cnt[c - 'a']); |
| 195 | + } |
| 196 | + long ans = 0; |
| 197 | + for (int i = 1; i <= mx; ++i) { |
| 198 | + long x = 1; |
| 199 | + for (int j = 0; j < 26; ++j) { |
| 200 | + if (cnt[j] >= i) { |
| 201 | + x = (x * (comb(cnt[j], i) + 1)) % MOD; |
| 202 | + } |
| 203 | + } |
| 204 | + ans = (ans + x - 1) % MOD; |
| 205 | + } |
| 206 | + return ans; |
| 207 | + } |
| 208 | +}; |
73 | 209 | ```
|
74 | 210 |
|
75 | 211 | ### **Go**
|
76 | 212 |
|
77 | 213 | ```go
|
78 |
| - |
| 214 | +const n = 1e4 + 1 |
| 215 | +const mod = 1e9 + 7 |
| 216 | +
|
| 217 | +var f = make([]int, n) |
| 218 | +var g = make([]int, n) |
| 219 | +
|
| 220 | +func qmi(a, k, p int) int { |
| 221 | + res := 1 |
| 222 | + for k != 0 { |
| 223 | + if k&1 == 1 { |
| 224 | + res = res * a % p |
| 225 | + } |
| 226 | + k >>= 1 |
| 227 | + a = a * a % p |
| 228 | + } |
| 229 | + return res |
| 230 | +} |
| 231 | +
|
| 232 | +func init() { |
| 233 | + f[0], g[0] = 1, 1 |
| 234 | + for i := 1; i < n; i++ { |
| 235 | + f[i] = f[i-1] * i % mod |
| 236 | + g[i] = qmi(f[i], mod-2, mod) |
| 237 | + } |
| 238 | +} |
| 239 | +
|
| 240 | +func comb(n, k int) int { |
| 241 | + return (f[n] * g[k] % mod) * g[n-k] % mod |
| 242 | +} |
| 243 | +
|
| 244 | +func countGoodSubsequences(s string) (ans int) { |
| 245 | + cnt := [26]int{} |
| 246 | + mx := 1 |
| 247 | + for _, c := range s { |
| 248 | + cnt[c-'a']++ |
| 249 | + mx = max(mx, cnt[c-'a']) |
| 250 | + } |
| 251 | + for i := 1; i <= mx; i++ { |
| 252 | + x := 1 |
| 253 | + for _, v := range cnt { |
| 254 | + if v >= i { |
| 255 | + x = (x * (comb(v, i) + 1)) % mod |
| 256 | + } |
| 257 | + } |
| 258 | + ans = (ans + x - 1) % mod |
| 259 | + } |
| 260 | + return |
| 261 | +} |
| 262 | +
|
| 263 | +func max(a, b int) int { |
| 264 | + if a > b { |
| 265 | + return a |
| 266 | + } |
| 267 | + return b |
| 268 | +} |
79 | 269 | ```
|
80 | 270 |
|
81 | 271 | ### **...**
|
|
0 commit comments