Number of Substrings With Fixed Ratio
MediumUpdated: Aug 2, 2025
Practice on:
Problem
You are given a binary string s, and two integers num1 and num2. num1
and num2 are coprime numbers.
A ratio substring is a substring of s where the ratio between the number of 0's and the number of 1's in the substring is exactly num1 : num2.
- For example, if
num1 = 2andnum2 = 3, then"01011"and"1110000111"are ratio substrings, while"11000"is not.
Return _the number ofnon-empty ratio substrings of _s.
Note that:
- A substring is a contiguous sequence of characters within a string.
- Two values
xandyare coprime ifgcd(x, y) == 1wheregcd(x, y)is the greatest common divisor ofxandy.
Examples
Example 1:
Input: s = "0110011", num1 = 1, num2 = 2
Output: 4
Explanation: There exist 4 non-empty ratio substrings.
- The substring s[0..2]: "_011_ 0011". It contains one 0 and two 1's. The ratio is 1 : 2.
- The substring s[1..4]: "0 _110_ 011". It contains one 0 and two 1's. The ratio is 1 : 2.
- The substring s[4..6]: "0110 _011_ ". It contains one 0 and two 1's. The ratio is 1 : 2.
- The substring s[1..6]: "0 _110011_ ". It contains two 0's and four 1's. The ratio is 2 : 4 == 1 : 2.
It can be shown that there are no more ratio substrings.
Example 2:
Input: s = "10101", num1 = 3, num2 = 1
Output: 0
Explanation: There is no ratio substrings of s. We return 0.
Constraints:
1 <= s.length <= 10^51 <= num1, num2 <= s.lengthnum1andnum2are coprime integers.
Solution
Method 1 – Prefix Hashing and Counting
Intuition
For a substring s[l..r], let c0 = number of 0's, c1 = number of 1's. The substring is valid if c0:num1 == c1:num2, i.e., c0num2 == c1num1. We can use prefix sums and a hash map to count the number of valid substrings ending at each position.
Approach
- For each prefix, keep track of (num2 * count_0 - num1 * count_1).
- For each position, the number of previous prefixes with the same value gives the number of valid substrings ending here.
- Use a hash map to count occurrences of each prefix value.
Code
C++
#include <unordered_map>
class Solution {
public:
int fixedRatioSubstrings(string s, int num1, int num2) {
unordered_map<int, int> count;
count[0] = 1;
int c0 = 0, c1 = 0, ans = 0;
for (char ch : s) {
if (ch == '0') c0++;
else c1++;
int key = num2 * c0 - num1 * c1;
ans += count[key];
count[key]++;
}
return ans;
}
};
Go
func fixedRatioSubstrings(s string, num1, num2 int) int {
count := map[int]int{0: 1}
c0, c1, ans := 0, 0, 0
for _, ch := range s {
if ch == '0' { c0++ } else { c1++ }
key := num2*c0 - num1*c1
ans += count[key]
count[key]++
}
return ans
}
Java
import java.util.*;
class Solution {
public int fixedRatioSubstrings(String s, int num1, int num2) {
Map<Integer, Integer> count = new HashMap<>();
count.put(0, 1);
int c0 = 0, c1 = 0, ans = 0;
for (char ch : s.toCharArray()) {
if (ch == '0') c0++;
else c1++;
int key = num2 * c0 - num1 * c1;
ans += count.getOrDefault(key, 0);
count.put(key, count.getOrDefault(key, 0) + 1);
}
return ans;
}
}
Kotlin
class Solution {
fun fixedRatioSubstrings(s: String, num1: Int, num2: Int): Int {
val count = mutableMapOf(0 to 1)
var c0 = 0; var c1 = 0; var ans = 0
for (ch in s) {
if (ch == '0') c0++ else c1++
val key = num2 * c0 - num1 * c1
ans += count.getOrDefault(key, 0)
count[key] = count.getOrDefault(key, 0) + 1
}
return ans
}
}
Python
class Solution:
def fixedRatioSubstrings(self, s: str, num1: int, num2: int) -> int:
from collections import defaultdict
count = defaultdict(int)
count[0] = 1
c0 = c1 = ans = 0
for ch in s:
if ch == '0':
c0 += 1
else:
c1 += 1
key = num2 * c0 - num1 * c1
ans += count[key]
count[key] += 1
return ans
Rust
use std::collections::HashMap;
impl Solution {
pub fn fixed_ratio_substrings(s: String, num1: i32, num2: i32) -> i32 {
let mut count = HashMap::new();
count.insert(0, 1);
let (mut c0, mut c1, mut ans) = (0, 0, 0);
for ch in s.chars() {
if ch == '0' { c0 += 1; } else { c1 += 1; }
let key = num2 * c0 - num1 * c1;
ans += *count.get(&key).unwrap_or(&0);
*count.entry(key).or_insert(0) += 1;
}
ans
}
}
TypeScript
class Solution {
fixedRatioSubstrings(s: string, num1: number, num2: number): number {
const count = new Map<number, number>()
count.set(0, 1)
let c0 = 0, c1 = 0, ans = 0
for (const ch of s) {
if (ch === '0') c0++
else c1++
const key = num2 * c0 - num1 * c1
ans += count.get(key) ?? 0
count.set(key, (count.get(key) ?? 0) + 1)
}
return ans
}
}
Complexity
- ⏰ Time complexity:
O(n) - 🧺 Space complexity:
O(n)