Number of substrings having an equal number of lowercase and uppercase letters


In this problem, we need to count the total number of strings of the given string containing an equal number of lowercase and uppercase characters. The naïve approach to solving the problem is to find all substrings and count the total number of substrings with an equal number of lowercase and uppercase characters.

The efficient approach is using the subarray sum problem. We can consider lowercase characters as -1 and uppercase characters as +1, and we will learn both approaches to solve the problem.

Problem statement- We have given string str containing the lowercase and uppercase alphabetical characters. We need to count the total number of substrings that contains an equal number of lowercase and uppercase characters.

Sample examples

Input – str = ‘TutOR’

Output – 4

Explanation–We can get ‘Tu’, ‘TutO’, ‘tO’, and ‘utOR’ total 4 substrings containing an equal number of lowercase and uppercase character

Input – str = ‘abcd’

Output – 0

Explanation– We can’t get any substring containing equal lowercase and uppercase characters as the string contains only lowercase characters

Input – str = ‘XYz’

Output – 1

Explanation– We can get only the ‘Yz’ substring.

Approach 1

This is the naïve approach to solving the problem. We will use 3 nested loops to find all substrings of the given string. We will check whether each substring contains an equal number of lowercase and uppercase characters. If yes, we increase the value of the count by 1.

Algorithm

  • Define the ‘cnt’ variable and initialize it with zero.

  • Use the for loop to iterate through the string

  • In the loop, define the ‘upperCase’ and ‘lowerCase’ variables and initialize them with zero

  • Add another nested loop to start iterating the string from the ith index. So, we can take a substring from the I to the jth index

  • In the nested loop, if the character is in uppercase, increase the value of the uppercase by 1. Otherwise, increase the value of a lowercase variable by 1.

  • If the value of the ‘upperCase’ and ‘lowerCase’ variables are equal, increase the ‘cnt’ value by 1.

  • Return the value of ‘cnt’.

Example

#include <iostream>
using namespace std;
// function to find the total number of substrings that have an equal number of lowercase and uppercase characters
int totalSubStrings(string &str, int N){
   // variable to store the count.
   int cnt = 0;
   for (int i = 0; i < str.length(); i++){
      // variable to store the count of upper and lower case characters
      int upperCase = 0;
      int lowerCase = 0;
      for (int j = i; j < str.length(); j++){
         // If the character is in uppercase, then increment upperCase
         if (str[j] >= 'A' && str[j] <= 'Z')
            upperCase++;
         else
            lowerCase++;
         // If the count of uppercase and lowercase characters is equal, then increment cnt
            if (upperCase == lowerCase)
               cnt++;
         }
      }
   return cnt;
}
int main(){
   string str = "TutOR";
   cout << "The total number of substrings that have equal lowercase and uppercase characters is " << totalSubStrings(str, str.length());
   return 0;
}

Output

The total number of substrings that have equal lowercase and uppercase characters is 4

Time complexity – O(N^2), as we use nested loops to find all substrings.

Space complexity – O(1), as we use constant space.

Approach 2

In this approach, we will optimize the code of the above approach to improve the time complexity of the solution. We will consider uppercase characters as +1 and lowercase characters as -1. Also, we will use the map data structure to store the frequency of the previous prefix sums. If we find a prefix sum equal to zero or the same as any prefix sum which is stored in the map, we can increase the count value.

Algorithm

  • Define the ‘sum’ map containing integers as a key-pairs value.

  • Define the ‘cnt’ variable and initialize with zero to store the total substring with equal lowercase and uppercase characters. Also, define the ‘current’ variable to store the prefix sum

  • Start traversing the string. Add 1 to the ‘current’ variable if the current character is in uppercase. Otherwise, subtract 1 from the ‘current’ character.

  • If the value of ‘current’ is 0, we found the substring and increased the value of ‘cnt’ by 1

  • Check if the map contains the ‘current’ as a key. If yes, get its value and add it to the ‘cnt’ variable.

  • Increase the value of the ‘current’ key by 1 in the map.

Example

For a better understanding of the problem. Let’s debug the example input, which is ‘TutOR’.

So, when we start iterating the string, the ‘current’ value will become 0 at the first index. So, increase the value of ‘cnt’ by 1. Again, it will be zero at the 3rd index. So, increase the ‘cnt’ value by 1, which will become 2. We also got 0 previously, so it exists in the map, and we need to add that value to the ‘cnt’. So, it will become 3.

When we reach the 4th index, the ‘current’ variable’s value will be 1, and we get it again, as we got it at the 0th index. So, add ‘1’ to the ‘cnt’.

The basic logic is that if ‘current’ is 0, we get the substring. If we got the ‘current’ value again, we could take the string between both indexes as ‘current – current’ is zero.

#include <iostream>
#include <unordered_map>
using namespace std;

// function to find the total number of substrings that have an equal number of lowercase and uppercase characters
int totalSubStrings(string &str, int N){
    // map to store the frequency of sums generated by the difference in the count of lowercase and uppercase characters
    unordered_map<int, int> sum;
    // to store the count of substrings that have an equal number of lowercase and uppercase characters
    int cnt = 0;
    // current sum
    int current = 0;
    for (int i = 0; i < N; i++){
        // If the character is uppercase, then increment the current value
        if (str[i] >= 'A' and str[i] <= 'Z'){
            current++;
        }
        // Otherwise, decrement the current value
        else
            current--;
        // If the current value is 0, then a substring is found. So, increment the count by 1
        if (current == 0)
            cnt++;
        // If the current value exists in the map, then update the cnt by adding the frequency of the current value
        if (sum[current]){
            cnt += (sum[current]);
        }
        // Increment the frequency of the current value in the map
        sum[current]++;
    }
    return cnt;
}
int main(){
    string str = "TutOR";
    cout << "The total number of substrings that have equal lowercase and uppercase characters is " << totalSubStrings(str, str.length());
    return 0;
}

Output

The total number of substrings that have equal lowercase and uppercase characters is 4

Time complexity – O(N), as we traverse the string once.

Space complexity – O(N), as we use the map to store prefix sum. In the worst-case scenario, when the string contains all lowercase or uppercase characters, we need O(N) space.

We learned to solve the above problem using two different approaches. The first approach checks all strings using nested loops, and the second approach is more efficient than the first in time complexity but more space-consuming.

Updated on: 10-Aug-2023

152 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements