Remove at most K letter to equalize the frequency

Last Updated : 11 Jun, 2024

Given a string S of length n, consisting of lowercase English letters. The task is to check if it is possible to remove at most K letter from S such that the frequency of every remaining letter is equal.

Examples:

Input: S = "aabbcc", K = 2
Output: True
Explanation: We can remove one 'a' and one 'b' to get "abbcc", where the frequency of each remaining letter is 2

Input: S = "eaabbccd", K = 1
Output: False
Explanation: Removing any one letter won't be enough to make the frequency of every remaining letter equal.

Approach:

The goal is to see if we can remove up to K letters to make all remaining letters in S have the same frequency. To achieve this, we can follow these steps:

  1. Count the frequency of each letter in the string.
  2. Determine the unique frequencies of these counts.
  3. Analyze if adjusting these frequencies by removing up to K letters can make the frequencies equal.

Step-by-Step Approach:

  • Count the frequency of each character in the string S.
  • Record the frequencies in a map or array.
  • Determine the unique frequency values.
  • Check if you can make all frequencies equal by removing at most K letters:
    • If there's only one unique frequency, return True.
    • If there are two unique frequencies, check the conditions under which you can adjust them to become equal by removing up to K letters.
    • If more than two unique frequencies exist, it's not possible to equalize them by removing K letters, return False.

Below is the implementation of the above approach:

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

bool canMakeEqualFrequency(string S, int K)
{
    unordered_map<char, int> frequencyMap;

    // Count frequency of each character
    for (char c : S) {
        frequencyMap[c]++;
    }

    // Store frequencies in a vector
    vector<int> frequencies;
    for (auto entry : frequencyMap) {
        frequencies.push_back(entry.second);
    }

    // Sort frequencies to easily check the conditions
    sort(frequencies.begin(), frequencies.end());

    // If all frequencies are already the same
    if (frequencies.front() == frequencies.back()) {
        return true;
    }

    // We now check two cases: reducing the higher frequency
    // or increasing the lower frequency
    int minFreq = frequencies.front();
    int maxFreq = frequencies.back();

    // Case 1: Remove characters to reduce the higher
    // frequency
    int removeMaxFreq = maxFreq - minFreq;
    int maxFreqCount = count(frequencies.begin(),
                             frequencies.end(), maxFreq);
    if ((removeMaxFreq * maxFreqCount <= K)
        && (frequencies[frequencies.size() - maxFreqCount
                        - 1]
            == minFreq)) {
        return true;
    }

    // Case 2: Remove characters to increase the lower
    // frequency
    int minFreqCount = count(frequencies.begin(),
                             frequencies.end(), minFreq);
    if ((minFreq * minFreqCount <= K)
        && (frequencies[minFreqCount] == maxFreq)) {
        return true;
    }

    return false;
}

int main()
{
    string S1 = "aabbcc";
    int K1 = 2;
    cout << "Example 1: "
         << (canMakeEqualFrequency(S1, K1) ? "True"
                                           : "False")
         << endl;

    string S2 = "eaabbccd";
    int K2 = 1;
    cout << "Example 2: "
         << (canMakeEqualFrequency(S2, K2) ? "True"
                                           : "False")
         << endl;

    return 0;
}
Java
import java.util.*;

public class Main {

    public static boolean canMakeEqualFrequency(String S, int K) {
        // Count frequency of each character
        Map<Character, Integer> frequencyMap = new HashMap<>();
        for (char c : S.toCharArray()) {
            frequencyMap.put(c, frequencyMap.getOrDefault(c, 0) + 1);
        }

        // Store frequencies in a list
        List<Integer> frequencies = new ArrayList<>(frequencyMap.values());
        Collections.sort(frequencies);

        // If all frequencies are already the same
        if (frequencies.get(0).equals(frequencies.get(frequencies.size() - 1))) {
            return true;
        }

        // We now check two cases: reducing the higher frequency or increasing the lower frequency
        int minFreq = frequencies.get(0);
        int maxFreq = frequencies.get(frequencies.size() - 1);

        // Case 1: Remove characters to reduce the higher frequency
        int removeMaxFreq = maxFreq - minFreq;
        int maxFreqCount = Collections.frequency(frequencies, maxFreq);
        if ((removeMaxFreq * maxFreqCount <= K) && (frequencies.get(frequencies.size() - maxFreqCount - 1) == minFreq)) {
            return true;
        }

        // Case 2: Remove characters to increase the lower frequency
        int minFreqCount = Collections.frequency(frequencies, minFreq);
        if ((minFreq * minFreqCount <= K) && (frequencies.get(minFreqCount) == maxFreq)) {
            return true;
        }

        return false;
    }

    public static void main(String[] args) {
        String S1 = "aabbcc";
        int K1 = 2;
        System.out.println("Example 1: " + (canMakeEqualFrequency(S1, K1) ? "True" : "False"));

        String S2 = "eaabbccd";
        int K2 = 1;
        System.out.println("Example 2: " + (canMakeEqualFrequency(S2, K2) ? "True" : "False"));
    }
}

// This code is contributed by Shivam Gupta

Output
Example 1: True
Example 2: False

Time Complexity: O(n + m*logm) Counting frequencies takes O(n). Sorting the frequencies takes O(m*logm) where m is the number of unique characters (at most 26 for lowercase English letters).

Auxiliary Space: O(m) The frequency map and the vector to store frequencies both use O(m) space.


Comment