题目链接:
https://codeforces.com/problemset/problem/1622/D
题目大意:
给定一个只含01的字符串,你可以选择其中一个包含 \(k\) 个 1 的子串,然后对这个子串重新排序,计算可以获得多少个不同的字符串。
思路:
因为符合条件的子串有可能有很多个,如果只是选取包含 \(k\) 个 1 的子串然后重新排列,则需要判断每个子串重新排序后形成的字符串是否重复,这个很难实现。
于是考虑固定子串的起点和终点,这样子每个子串重新排序之后形成的字符串就不会出现重复的情况,我们可以假设子串的起点和终点都是 1,可以通过嵌套循环去选取起点和终点,然后计算能形成的不同子串的数量就可以了。
而计算一个子串重排列后能够形成多少个不同的字符串,需要知道子串含 1 的数量,记 0 的数量为 a,1 的数量为 b,那么总共能形成 \(C_{a + b}^b\) 不同的字符串,这样子长度为 \(a + b\),包含 \(b\) 个 1 数量的子串能形成的不同的字符串的数量就可以直接求出来。
代码:
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int n, k;
string s;
void solve(){
int ans = 1;
cin >> n >> k >> s;
vector <int> p(n + 1, 0);
for (int i = 1; i <= n; i++)
p[i] = p[i - 1] + (s[i - 1] - '0'); //求出 1 的数量的前缀和
if (p[n] < k){
cout << "1\n";
return;
}
vector <vector <int> > cnt(n + 1);
for (int i = 0; i <= n; i++){ //计算长为 i,包含 j 个 1 的字符串重排列后能形成不同的字符串
cnt[i].resize(i + 1);
cnt[i][0] = cnt[i][i] = 1;
for (int j = 1; j < i; j++)
cnt[i][j] = (cnt[i - 1][j] + cnt[i - 1][j - 1]) % mod;
}
for (int i = 0; i < n; i++){
for (int j = i + 1; j < n; j++){
int a = j - i - 1;
int b = p[j + 1] - p[i];
if (b > k) continue;
b -= ((s[i] - '0') ^ 1) + ((s[j] - '0') ^ 1); //固定起点和终点为 1
if(b < 0 || a < 0 || b > a) continue;
ans = (ans + cnt[a][b]) % mod;
}
}
cout << ans << "\n";
}
int main(){
solve();
return 0;
}