1513. Number of Substrings With Only 1s
Problem Description
You are given a binary string s
that consists only of characters '0' and '1'. Your task is to count the total number of substrings that contain only '1' characters.
For example, if you have a string "0110111", you need to find all possible substrings that are made up entirely of '1's. In this case:
- From the first group "11": you can form substrings "1", "1", and "11" (total: 3)
- From the second group "111": you can form substrings "1", "1", "1", "11", "11", and "111" (total: 6)
- Total count: 3 + 6 = 9
The key insight is that for a consecutive sequence of n
ones, the number of valid substrings is n * (n + 1) / 2
. This is because:
- You have
n
substrings of length 1 - You have
n-1
substrings of length 2 - You have
n-2
substrings of length 3 - And so on...
- Which sums to
n + (n-1) + (n-2) + ... + 1 = n * (n + 1) / 2
The solution iterates through the string character by character. It maintains a counter cnt
that tracks the current length of consecutive '1's. For each '1' encountered, it increments the counter and adds the counter value to the answer. This works because at each position ending with '1', the number of valid substrings ending at that position equals the length of the current consecutive sequence of '1's.
Since the answer can be very large, you need to return the result modulo 10^9 + 7
.
Intuition
When we see a problem asking for counting substrings with a specific property, we should think about how substrings are formed and counted efficiently.
Let's start with a simple observation: if we have a sequence of consecutive '1's, like "111", how many valid substrings can we form? We can pick:
- Any single '1': positions 0, 1, 2 → 3 substrings
- Any two consecutive '1's: positions (0,1), (1,2) → 2 substrings
- All three '1's: positions (0,1,2) → 1 substring
- Total: 3 + 2 + 1 = 6 substrings
This follows the pattern n + (n-1) + ... + 1 = n*(n+1)/2
for a sequence of length n
.
But wait - there's a more elegant way to think about this! Instead of finding each group of consecutive '1's and applying the formula, we can count incrementally as we traverse the string.
The key insight: when we're at position i
and it's a '1', how many valid substrings end at this position? The answer is exactly the number of consecutive '1's we've seen so far (including the current one). Why? Because each valid substring ending at position i
must start from some position within the current consecutive sequence of '1's.
For example, in "0111":
- At index 1 (first '1'): 1 substring ends here: "1"
- At index 2 (second '1'): 2 substrings end here: "1" and "11"
- At index 3 (third '1'): 3 substrings end here: "1", "11", and "111"
- Total: 1 + 2 + 3 = 6
This naturally gives us the same result as the formula 3*(3+1)/2 = 6
, but we compute it incrementally! We just maintain a counter that tracks consecutive '1's, reset it when we see a '0', and keep adding the counter value to our answer. This approach is both intuitive and efficient with O(n)
time and O(1)
space.
Learn more about Math patterns.
Solution Approach
The implementation uses a simple linear scan with two variables to solve the problem efficiently:
-
Initialize two variables:
ans
: Accumulates the total count of valid substringscnt
: Tracks the length of the current consecutive sequence of '1's
-
Iterate through each character in the string:
for c in s:
-
For each character, apply the counting logic:
-
If the character is '1':
if c == "1": cnt += 1
Increment the counter by 1, extending the current sequence of consecutive '1's.
-
If the character is '0':
else: cnt = 0
Reset the counter to 0, as the consecutive sequence is broken.
-
-
Add the counter value to the answer:
ans += cnt
This is the crucial step - we add
cnt
to our answer after processing each character. Whencnt
is non-zero (we're in a sequence of '1's), this adds the number of valid substrings ending at the current position. -
Return the result modulo
10^9 + 7
:return ans % (10**9 + 7)
Why this works:
- When we encounter a '1' and
cnt
becomesk
, it means there are exactlyk
valid substrings ending at this position (substrings of length 1, 2, ..., k starting from different positions in the current consecutive sequence) - When we encounter a '0',
cnt
becomes 0, so we add 0 to the answer (no valid substrings end at a '0') - The cumulative sum naturally gives us the total count of all valid substrings
Time Complexity: O(n)
where n is the length of the string - we traverse the string once.
Space Complexity: O(1)
- we only use two integer variables regardless of input size.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the string s = "01101"
.
We'll track two variables:
cnt
: counts consecutive '1'sans
: accumulates total valid substrings
Step-by-step process:
Index | Char | cnt (before) | cnt (after) | ans (before) | ans (after) | Explanation |
---|---|---|---|---|---|---|
0 | '0' | 0 | 0 | 0 | 0 | It's a '0', so cnt stays 0. Add 0 to ans. |
1 | '1' | 0 | 1 | 0 | 1 | It's a '1', so cnt becomes 1. Add 1 to ans (substring "1" ends here). |
2 | '1' | 1 | 2 | 1 | 3 | It's a '1', so cnt becomes 2. Add 2 to ans (substrings "1" and "11" end here). |
3 | '0' | 2 | 0 | 3 | 3 | It's a '0', so cnt resets to 0. Add 0 to ans. |
4 | '1' | 0 | 1 | 3 | 4 | It's a '1', so cnt becomes 1. Add 1 to ans (substring "1" ends here). |
Final answer: 4
Let's verify by listing all valid substrings:
- From indices 1-2: "1" (at index 1), "1" (at index 2), "11" (indices 1-2) → 3 substrings
- From index 4: "1" (at index 4) → 1 substring
- Total: 3 + 1 = 4 ✓
The key insight is that when cnt = k
, there are exactly k
valid substrings ending at the current position:
- When cnt = 1: one substring of length 1
- When cnt = 2: one substring of length 2 and one of length 1
- When cnt = 3: one substring of length 3, one of length 2, and one of length 1
- And so on...
This incremental counting naturally accumulates to the same result as counting each group separately using the formula n*(n+1)/2.
Solution Implementation
1class Solution:
2 def numSub(self, s: str) -> int:
3 """
4 Count the number of substrings that contain only '1's.
5
6 For each position, if it's '1', we extend the current sequence
7 and add the length to the answer (all substrings ending at this position).
8 If it's '0', we reset the counter.
9
10 Args:
11 s: Binary string containing only '0's and '1's
12
13 Returns:
14 Number of substrings with only '1's, modulo 10^9 + 7
15 """
16 MOD = 10**9 + 7
17 total_count = 0 # Total number of valid substrings
18 consecutive_ones = 0 # Length of current consecutive '1's sequence
19
20 # Iterate through each character in the string
21 for char in s:
22 if char == "1":
23 # Extend the current sequence of '1's
24 consecutive_ones += 1
25 # Add all substrings ending at current position
26 # (consecutive_ones represents how many valid substrings end here)
27 total_count += consecutive_ones
28 else:
29 # Reset counter when we encounter '0'
30 consecutive_ones = 0
31
32 # Return result modulo 10^9 + 7 to prevent overflow
33 return total_count % MOD
34
1class Solution {
2 /**
3 * Counts the number of substrings consisting only of '1's in the given binary string.
4 * For a continuous segment of n '1's, the number of valid substrings is n*(n+1)/2.
5 * This algorithm efficiently calculates this by accumulating counts as it traverses.
6 *
7 * @param s The input binary string containing only '0's and '1's
8 * @return The total count of substrings with only '1's, modulo 10^9 + 7
9 */
10 public int numSub(String s) {
11 // Define modulo constant to prevent integer overflow
12 final int MODULO = (int) 1e9 + 7;
13
14 // Total count of valid substrings
15 int totalCount = 0;
16
17 // Current consecutive '1's count ending at current position
18 int consecutiveOnes = 0;
19
20 // Iterate through each character in the string
21 for (int i = 0; i < s.length(); i++) {
22 // If current character is '1', increment consecutive count
23 // Otherwise, reset consecutive count to 0
24 if (s.charAt(i) == '1') {
25 consecutiveOnes++;
26 } else {
27 consecutiveOnes = 0;
28 }
29
30 // Add current consecutive count to total
31 // This represents all substrings of '1's ending at position i
32 totalCount = (totalCount + consecutiveOnes) % MODULO;
33 }
34
35 return totalCount;
36 }
37}
38
1class Solution {
2public:
3 int numSub(string s) {
4 // Constants
5 const int MOD = 1000000007; // 10^9 + 7 for modulo operation
6
7 // Variables to track the result and current consecutive ones
8 int totalCount = 0; // Total number of valid substrings
9 int consecutiveOnes = 0; // Current length of consecutive '1's
10
11 // Iterate through each character in the string
12 for (const char& currentChar : s) {
13 if (currentChar == '1') {
14 // If current character is '1', increment the consecutive count
15 consecutiveOnes++;
16 // Add the number of new substrings ending at current position
17 totalCount = (totalCount + consecutiveOnes) % MOD;
18 } else {
19 // If current character is '0', reset the consecutive count
20 consecutiveOnes = 0;
21 }
22 }
23
24 return totalCount;
25 }
26};
27
1/**
2 * Counts the number of substrings containing only '1's in a binary string.
3 * Uses dynamic programming approach where each '1' contributes to multiple substrings.
4 * @param s - The input binary string containing only '0's and '1's
5 * @returns The total count of substrings with only '1's, modulo 10^9 + 7
6 */
7function numSub(s: string): number {
8 // Modulo value to prevent integer overflow
9 const MODULO: number = 10 ** 9 + 7;
10
11 // Total count of valid substrings
12 let totalCount: number = 0;
13
14 // Count of consecutive '1's ending at current position
15 let consecutiveOnes: number = 0;
16
17 // Iterate through each character in the string
18 for (const character of s) {
19 // If current character is '1', increment consecutive count
20 // Otherwise, reset to 0 (breaking the sequence)
21 consecutiveOnes = character === '1' ? consecutiveOnes + 1 : 0;
22
23 // Add current consecutive count to total
24 // Each new '1' forms consecutiveOnes new substrings
25 totalCount = (totalCount + consecutiveOnes) % MODULO;
26 }
27
28 return totalCount;
29}
30
Time and Space Complexity
Time Complexity: O(n)
where n
is the length of the string s
. The algorithm iterates through each character in the string exactly once, performing constant-time operations (comparison, addition, modulo) for each character.
Space Complexity: O(1)
. The algorithm uses only a fixed amount of extra space regardless of the input size. It maintains three variables (ans
, cnt
, and c
) which don't scale with the input size. The space used for the input string itself is not counted as auxiliary space.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Apply the Modulo Operation
One of the most common mistakes is forgetting to apply the modulo operation or applying it incorrectly. The problem explicitly states that the answer should be returned modulo 10^9 + 7
to handle large numbers.
Incorrect Implementation:
def numSub(self, s: str) -> int:
total_count = 0
consecutive_ones = 0
for char in s:
if char == "1":
consecutive_ones += 1
total_count += consecutive_ones
else:
consecutive_ones = 0
return total_count # Missing modulo operation!
Why this fails: For very long strings with many '1's, the result can exceed the integer limits in some systems or expected output format, causing incorrect results or runtime errors.
Correct Solution:
def numSub(self, s: str) -> int:
MOD = 10**9 + 7
total_count = 0
consecutive_ones = 0
for char in s:
if char == "1":
consecutive_ones += 1
total_count += consecutive_ones
else:
consecutive_ones = 0
return total_count % MOD # Apply modulo at the end
2. Integer Overflow in Intermediate Calculations
While Python handles large integers automatically, in other languages or when optimizing, you might try to use the formula n * (n + 1) / 2
directly for each group of consecutive '1's. This can cause overflow issues.
Potentially Problematic Approach:
def numSub(self, s: str) -> int:
MOD = 10**9 + 7
total_count = 0
consecutive_ones = 0
for i, char in enumerate(s):
if char == "1":
consecutive_ones += 1
# Check if next character is '0' or end of string
if i == len(s) - 1 or s[i + 1] == "0":
# Using formula directly - can cause overflow in other languages
total_count += (consecutive_ones * (consecutive_ones + 1)) // 2
consecutive_ones = 0
return total_count % MOD
Better Solution (with intermediate modulo):
def numSub(self, s: str) -> int:
MOD = 10**9 + 7
total_count = 0
consecutive_ones = 0
for char in s:
if char == "1":
consecutive_ones += 1
# Apply modulo during accumulation to prevent overflow
total_count = (total_count + consecutive_ones) % MOD
else:
consecutive_ones = 0
return total_count
3. Misunderstanding the Counting Logic
Some might incorrectly think they need to count each group of '1's separately and then apply the formula, leading to more complex and error-prone code.
Overcomplicated Approach:
def numSub(self, s: str) -> int:
MOD = 10**9 + 7
groups = []
i = 0
# Find all groups of consecutive '1's
while i < len(s):
if s[i] == "1":
j = i
while j < len(s) and s[j] == "1":
j += 1
groups.append(j - i)
i = j
else:
i += 1
# Calculate sum for each group
total = 0
for length in groups:
total += (length * (length + 1)) // 2
return total % MOD
Why the simple approach is better: The incremental counting method is more elegant, requires less code, and naturally handles all cases without needing to explicitly identify groups or handle edge cases separately. It's also more memory-efficient as it doesn't store intermediate group information.
Which of the following problems can be solved with backtracking (select multiple)
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!