小美定义一个 01 串的权值为:每次操作选择一位取反,使得相邻字符都不相等的最小操作次数。
例如,"10001"的权值是 1,因为只需要修改一次:对第三个字符取反即可。
现在小美拿到了一个 01 串,她希望你求出所有非空连续子串的权值之和,你能帮帮她吗?
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 256M,其他语言512M
输入描述:
一个仅包含'0'和'1'的字符串,长度不超过 2000。
输出描述:
所有非空子串的权值和。
示例1
输入例子:
10001
输出例子:
8
例子说明:
长度为 2 的子串中,有 2 个"00"的权值是 1。
长度为 3 的 3 个子串权值都是 1。
长度为 4 的 2 个子串权值都是 1。
长度为 5 的 1 个子串权值是 1。
总权值之和为 2+3+2+1=8
可以使用动态规划来解决。
首先,定义一个二维数组 dp,其中 dp[i][j] 表示从第 i 个字符到第 j 个字符(包含两端)的子串的权值和。
对于每个字符 s[j],我们可以分两种情况讨论:
- 如果 s[j] 和 s[j-1] 相等,说明当前字符不需要取反,那么 dp[i][j] = dp[i][j-1];
- 如果 s[j] 和 s[j-1] 不相等,说明当前字符需要取反,那么 dp[i][j] = dp[i][j-1] + 1。
最终的答案就是 dp[0][n-1],其中 n 是字符串 s 的长度。
下面是具体的实现代码:
cpp
1 | #include <bits/stdc++.h> |
2 | using namespace std; |
3 | const int N = 2005; |
4 | int n; |
5 | char s[N]; |
6 | int dp[N][N]; |
7 | |
8 | int main() { |
9 | cin >> s+1; // 读入字符串,从第二个字符开始读入 |
10 | n = strlen(s+1); // 计算字符串长度 |
11 | |
12 | for (int i = 1; i <= n; i++) { |
13 | if (s[i] == s[i-1]) { |
14 | dp[i][i] = dp[i-1][i-1]; // 相等时权值不变 |
15 | } else { |
16 | dp[i][i] = dp[i-1][i-1] + 1; // 不相等时权值加一 |
17 | } |
18 | for (int j = i+1; j <= n; j++) { |
19 | if (s[j] == s[j-1]) { |
20 | dp[i][j] = dp[i][j-1]; // 相等时权值不变 |
21 | } else { |
22 | dp[i][j] = dp[i][j-1] + 1; // 不相等时权值加一 |
23 | } |
24 | } |
25 | } |
26 | cout << dp[0][n] << endl; // 输出最终答案 |
27 | return 0; |
28 | } |