计算X:Y比例的0和1子字符串


在这个问题中,我们将统计给定二进制字符串的子字符串,这些子字符串包含“0”和“1”字符的数量,且比例为X:Y。

朴素的方法是找到给定二进制字符串的所有子字符串,统计“0”和“1”的数量,并检查这些数量是否为X:Y比例。有效的方法使用前缀和技术来解决问题。

问题陈述 - 我们给定一个长度为bin_len的二进制字符串。我们需要统计具有0和1的数量之比为X:Y的子字符串。

示例

输入

bin_str = "00111001100"; X = 2; Y = 3;

输出

5

解释 - 具有“1”和“0”之比为2:3的子字符串为00111、01110、10011、11001和11100。

输入

bin_str = "11111"; X = 2; Y = 3;

输出

0

解释 - 二进制字符串仅包含“1”。因此,无法找到包含“0”和“1”之比为2:3的子字符串。

输入

bin_str = "00100"; X = 2; Y = 1;

输出

3

解释 - 我们可以取001、010和100子字符串。

方法1

在这种方法中,我们将使用两个嵌套循环来找到二进制字符串的所有子字符串。之后,我们将统计二进制字符串中“1”和“0”的数量。

如果以下条件对任何子字符串都成立,则我们可以将该子字符串考虑在答案中。

zeroCnt * Y == oneCnt * X

算法

步骤1 - 将“ans”初始化为0,以存储子字符串的数量。

步骤2 - 使用for循环遍历二进制字符串。另外,使用另一个循环获取从当前索引开始的每个子字符串。

步骤3 - 使用substr()方法获取特定长度的子字符串。

步骤4 - 将“zeroCnt”和“oneCnt”初始化为0,以存储子字符串中“1”和“0”的数量。

步骤5 - 使用第三个嵌套循环来统计“1”和“0”。如果当前字符为“1”,则递增“oneCnt”。否则,递增“ZeroCnt”。

步骤6 - 在第二个嵌套循环中,如果zeroCnt * Y == oneCnt * X为真,则将“ans”的值递增1。

步骤7 - 最后,从函数返回“ans”值。

示例

#include <bits/stdc++.h>
using namespace std;

int subStrCnt(string &bin_str, int X, int Y) {
    int ans = 0;
    // Get all substrings of the binary string
    for (int p = 0; p < bin_str.length(); p++) {
        for (int q = 1; q <= bin_str.length() - p; q++) {
            string sub_str = bin_str.substr(p, q);
            // Counting the number of zeros and ones in the substring
            int zeroCnt = 0, oneCnt = 0;
            for (int p = 0; p < sub_str.length(); p++) {
                if (sub_str[p] == '0')
                    zeroCnt++;
                else
                    oneCnt++;
            }
            // Checking whether the count of zeros and ones in the X:Y ratio
            if (zeroCnt * Y == oneCnt * X)
                ans++;
        }
    }
    return ans;
}
int main() {
    string bin_str = "00111001100";
    int X = 2;
    int Y = 3;
    cout << "The total substrings having 0's and 1's in the ratio X:Y is " << subStrCnt(bin_str, X, Y);
    return 0;
}

输出

The total substrings having 0's and 1's in the ratio X:Y is 5

时间复杂度 - O(N*N*N),其中O(N*N)用于获取所有子字符串,O(N)用于统计子字符串中的“1”和“0”。

空间复杂度 - O(N)用于存储子字符串。

方法2

在这种方法中,我们将“0”视为-Y,“1”视为-X。我们将根据“0”和“1”的数量找到-Y和X的和。如果和等于0,我们可以说我们找到了有效的子字符串。

此外,我们将使用map数据结构来存储前缀和。每当我们第二次找到任何前缀和值时,我们就可以获取和为0的索引之间的子字符串。

例如,二进制字符串为“00111001100”。我们在第0个和第5个索引处找到了相同的前缀和。因此,我们可以获取从第1个索引到第5个索引的子字符串。

算法

步骤1 - 初始化“binary”列表,以将二进制字符串转换为-Y和X格式。

步骤2 - 遍历字符串。如果当前字符为“0”,则在列表中插入“-Y”。否则,在列表中插入“X”。

步骤3 - 定义“pref”map以存储前缀和作为键,以及其值的出现次数。

步骤4 - 另外,初始化“ans”以存储子字符串的数量,并将“sum”初始化为0以存储前缀和。

步骤5 - 开始遍历字符串。将binary[p]添加到sum中。

步骤6 - 如果“sum”为0,则将“ans”递增1。

步骤7 - 如果sum键存在于map中,则将其值添加到“ans”中。

步骤8 - 将pref[sum]递增1。

步骤9 - 返回“ans”值。

示例

#include <bits/stdc++.h>
using namespace std;

int subStrCnt(string &bin_str, int X, int Y) {
    vector<int> binary;
    for (auto ch : bin_str) {
        if (ch == '0') { 
            // Change '0' to -Y
            binary.push_back(-Y);
        } else { 
            // Change '1' to X
            binary.push_back(X);
        }
    }
    // To store prefix sum
    unordered_map<int, int> pref;
    int ans = 0;
    int sum = 0;
    // Traverse string
    for (int p = 0; p < binary.size(); p++) {
        // Add a current element to sum
        sum += binary[p];
        // When aX - bY = 0, we found the substring
        if (sum == 0)
            ans += 1;
        // When prefix sum exists, we add the value to ans
        if (pref.find(sum) != pref.end())
            ans += pref[sum];
        // Increment the count of prefix sum
        pref[sum] += 1;
    }
    return ans;
}
int main() {
    string bin_str = "00111001100";
    int X = 2;
    int Y = 3;
    cout << "The total substrings having 0's and 1's in the ratio X:Y is " << subStrCnt(bin_str, X, Y);
    return 0;
}

输出

The total substrings having 0's and 1's in the ratio X:Y is 5

时间复杂度 - O(N)用于查找前缀和。

空间复杂度 - O(N)用于在map中存储前缀和。

优化后的方法有效地解决了问题,只需花费O(N)时间。但是,对于大型二进制字符串,它可能需要更多的空间。程序员可以尝试解决问题,以找到在二进制字符串中具有相同数量的“0”和“1”的子字符串。

更新于: 2023年7月17日

470 次查看

开启你的 职业生涯

通过完成课程获得认证

开始学习
广告