病毒检测
题目描述
小明最近在做病毒自动检测,他发现,在某些 的代码段的二进制表示中,如果包含子串并且恰好有 个,就有可能有潜在的病毒。 的二进制表示可能很大,并且子串可能很多,人工分析不可能,于是他想写个程序来先算算到底有多少个子串满足条件。如果子串内容相同,但是开始或者结束位置不一样,则被认为是不同的子串。
注:子串一定是连续的。例如"010"有 个子串,分别是 "0, "1", "0", "01", "10", "010"。
数据范围
解题思路
有如下结论成立
- 令 = 包含第 到第 个 且仅含 个 的字符串的数量。
- 令 = 第 到第 个 之间 的数量。
- 令 = 第 到第 个 之间 的数量。
- 则
故,只需预处理出第 到 第 个 之间 的个数,便可很容易得到仅包含 个 的子串的数量。
代码实现
long long solve(string s, int k) {
typedef long long ll;
int n = s.length();
int idx[n + 1], m = 0;
// 预处理出所有1的下标
// 通过“相邻”两个1下标得到这两个1之间的0的个数
for (int i = 1; i <= n; i++)
if (s[i - 1] == '1')idx[++m] = i;
idx[0] = 0, idx[m + 1] = n + 1;
ll res = 0;
// k为0时特殊处理
if (k == 0) {
for (int i = 1; i <= m + 1; i++) {
ll x = idx[i] - idx[i - 1] - 1;
res += (x + 1) * x / 2;
}
} else {
for (int l = 1, r = l + k - 1; r <= m; l++, r++)
res += (idx[l] - idx[l - 1]) * (idx[r + 1] - idx[r]);
}
return res;
}
时间复杂度:
空间复杂度:
优化实现
- “相邻”两个 之间 的个数可以在计算结果时边统计。
- 在计算包含第 到第 个 且仅含 个 的字符串的数量时,仅需知道第 到第 个 之间 的数量。
- 设 = 第 到第 个 之间 的个数, 的大小只需为 。
long long solve(string s, int k) {
typedef long long ll;
ll res = 0;
int dp[k + 1], m = 0;
dp[0] = 1;
for (char c: s) {
if (c == '1')m++, dp[m % (k + 1)] = 0;
if (m >= k)res += dp[(m - k) % (k + 1)];
// 统计第m到第m+1个1之间0的数量(数量含第m个1本身)
dp[m % (k + 1)]++;
}
return res;
}
时间复杂度:
空间复杂度:
考试成绩
题目描述
现在你的班级刚刚参加了一个只有单选题的考试。班级一共 个学生,考试有 个问题。每个题目都有 个可选答案(,,,,)。并且每个题目只有一个正确答案。每个题目的分数并不一样,第 个题目的分数用 表示。如果题目没答对该题会获得 分。 考试结束后,每个学生都记得自己的答案,但是他们还不知道正确答案是什么。如果非常乐观的考虑,他们班级最多可能得到多少分呢?
数据范围
解题思路
对于每道题,将选择人数最多的选项作为答案即可。
代码实现
/**
* @param ps 每位同学每道题的答案
* @param a 每道题的分数
*/
long long solve(vector<string> ps, vector<int> a) {
int m = a.size(), cnt[m + 1][5];
memset(cnt, 0, sizeof cnt);
for (auto &s: ps)
for (int i = 0; i < m; i++)
cnt[i][s[i] - 'A']++;
long long res = 0;
for (int i = 0; i < m; i++) {
for (int j = 1; j < 5; j++)
if (cnt[i][j] > cnt[i][0])cnt[i][0] = cnt[i][j];
res += a[i] * cnt[i][0];
}
return res;
}
时间复杂度:
空间复杂度:
石头碰撞
题目描述
给定一组石头,每个石头有一个正数的重量。每一轮开始的时候,选择两个石头一起碰撞,假定两个石头的重量为 ,,碰撞结果为
- 如果 ,碰撞结果为两个石头消失。
- 如果 ,碰撞结果两个石头消失,生成一个新的石头,新石头重量为 。
最终最多剩下一个石头为结束,问最小的剩余石头质量的可能是多少?
数据范围
第一行输入石头个数 ( )
第二行输入石头质量,以空格分割,石头质量总和为 ,
解题思路
转换为 背包问题,等价于,从 个石头中选,在重量之和不超过 个石头总重量一半的前提下,所能选取的石头的重量之和的最大值。
代码实现
int main() {
int n, sum = 0;
scanf("%d", &n);
int a[n + 1];
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sum += a[i];
}
int k = sum >> 1;
int dp[k + 1];
memset(dp, 0, sizeof dp);
for (int i = 1; i <= n; i++) {
for (int j = k; j >= a[i]; j--) {
dp[j] = max(dp[j], dp[j - a[i]] + a[i]);
}
}
printf("%d", sum - (dp[k] << 1));
return 0;
}
时间复杂度:
空间复杂度:
蓄水池大作战
题目描述
在你面前有 个蓄水池,他们组成了树形结构(由 条边连接)。蓄水池节点编号从 开始到 。对每个蓄水池节点来说,他的儿子蓄水池节点都摆放在他的下面,并且和它用水管相连,根据重力,水会向下流动。现在我们要在蓄水池上做一些操作:
- 把节点 填满水。然后v的所有儿子节点水也会被填满
- 清空节点 的水。然后v所有的父亲节点水都会被清空
- 询问每个蓄水池节点是否有水。
初始状态时候,每个节点都是空的。
现在我们会依次进行一系列操作,我们想提前知道每次操作后的结果,你能帮忙解决吗?
输入描述
第一行包含一个正整数 ,表示蓄水池节点的数量。
后面 行,每行有两个数字 (),表示蓄水池的连接关系。
接下来的一行包含一个整数 ,表示我们要进行的操作的数量。
最后的 行中,每行包含两个数字 ()。其中 表示操作类型(, 或者)。 表示操作对应的蓄水池节点。
输入数据保证合理,是一个连通的树。
提示:对于 ,默认小节点是大节点的父节点
输出描述
对于每个操作 (),输出一个数字 或者 。 表示 蓄水池节点有水, 表示没水。
解题思路
模拟即可
代码实现
void dfs(map<int, vector<int>> &mp, vector<int> &v, int x, int m) {
v[x] = m;
for (int i = 0; i < mp[x].size(); i++)
dfs(mp, v, mp[x][i], m);
}
int main() {
int n, a, b, q;
scanf("%d", &n);
map<int, vector<int>> son, pat;
vector<int> v(n + 1, 0);
while (--n) {
scanf("%d%d", &a, &b);
if (a > b) swap(a, b);
son[a].push_back(b);
pat[b].push_back(a);
}
scanf("%d", &q);
while (q--) {
scanf("%d%d", &a, &b);
if (a == 1) dfs(son, v, b, 1);
else if (a == 2) dfs(pat, v, b, 0);
else printf("%d\n", v[b]);
}
return 0;
}
时间复杂度:
空间复杂度:
END
文章声明:题目来源 牛客 平台,如有侵权,请联系删除!
文章文档:公众号 字节幺零二四 回复关键字可获取本文文档。