【每日一题】补档 CF1678B-Tokitsukaze and Good 01-String | 思维 | 简单

题目内容

原题链接

给定一个长度为 n n n 01 01 01 字符串, n n n 为偶数。
问至少要修改多少个字符,可以使得字符串被拆分成多个子段,每一个子段都是 0 0 0 或者都是 1 1 1 ,且长度都是偶数。

在最少修改次数的条件下,子段的最少数量是多少。

数据范围

  • 2 ≤ n ≤ 2 ⋅ 1 0 5 2\leq n\leq 2\cdot 10^5 2n2105
  • s s s 只包含字符 0 0 0 或字符 1 1 1

题解

本题需要拆分思考。

首先考虑第 n n n 个字符,其必然要和第 n − 1 n-1 n1 个字符相同。
所以当这两个字符相同后,考虑第 n − 2 n-2 n2 个字符,必然要和第 n − 3 n-3 n3 个字符相同。以此类推,第 i i i 个字符要和第 i + 1 i+1 i+1 个字符相同, i i i 为奇数。

所以最少操作次数是,将所有第 i i i 个字符和第 i + 1 i+1 i+1 个字符不相同的 ( i , i + 1 ) (i,i+1) (i,i+1) 要进行修改,所以这就是最小修改次数。

而最少子段数量,则必然是考虑第 i i i 个字符和第 i + 1 i+1 i+1 个字符不相同的 ( i , i + 1 ) (i,i+1) (i,i+1) 这些配对应该修改成什么,应该和靠近其最近的一个第 j j j 个字符和第 j + 1 j+1 j+1 个字符相同的 ( j , j + 1 ) (j,j+1) (j,j+1) 保持一致。

时间复杂度: O ( n ) O(n) O(n)

代码

#include 
using namespace std;

void solve() {
    int n;
    string s;

    cin >> n >> s;

    vector<int> vec;
    int ans1 = 0;
    for (int i = 0; i < n; i += 2) {
        if (s[i] == s[i + 1]) {
            vec.push_back(s[i] - '0');
        } else {
            ans1 += 1;
        }
    }

    int ans2 = 1;
    // 考虑这些新的需要分成多少段
    for (int i = 1; i < vec.size(); ++i) {
        if (vec[i] != vec[i - 1]) {
            ans2 += 1;
        }
    }

    cout << ans1 << " " << ans2 << "\n";
}

int main()
{
    int T;
    cin >> T;
    while (T--) solve();

    return 0;
}

你可能感兴趣的:(算法竞赛,c++,算法,思维)