|
| 1 | +--- |
| 2 | +id: minimum-substring-partition-of-equal-charater-frequency |
| 3 | +title: Minimum-substring-partition-of-equal-charater-frequency (LeetCode) |
| 4 | +sidebar_label: 3144-minimum-substring-partition-of-equal-charater-frequency |
| 5 | +tags: |
| 6 | + - Dynamic Programming |
| 7 | + - Geometry |
| 8 | + - Sorting |
| 9 | + - Sliding Window |
| 10 | +description: this discription is for the solution of minimum-substring-partition-of-equal-charater-frequency |
| 11 | +sidebar_position: 3144 |
| 12 | +--- |
| 13 | + |
| 14 | +## Problem Satement |
| 15 | + |
| 16 | +Given a string s, you need to partition it into one or more balanced |
| 17 | +substrings |
| 18 | +Substring |
| 19 | +A substring is a contiguous sequence of characters within a string. |
| 20 | + |
| 21 | +. For example, if `s == "ababcc"` then `("abab", "c", "c")`, `("ab", "abc", "c")`, and `("ababcc")` are all valid partitions, but `("a", "bab", "cc")`, `("aba", "bc", "c")`, and `("ab", "abcc")` are not. The unbalanced substrings are bolded. |
| 22 | + |
| 23 | +Return the minimum number of substrings that you can partition s into. |
| 24 | + |
| 25 | +Note: A balanced string is a string where each character in the string occurs the same number of times. |
| 26 | + |
| 27 | +**Example 1:** |
| 28 | + |
| 29 | +Input: s = "fabccddg" |
| 30 | +Output: 3 |
| 31 | + |
| 32 | +Explanation: |
| 33 | +We can partition the string s into 3 substrings in one of the following ways: ("fab, "ccdd", "g"), or ("fabc", "cd", "dg"). |
| 34 | + |
| 35 | +**Example 2:** |
| 36 | + |
| 37 | +Input: s = "abababaccddb" |
| 38 | +Output: 2 |
| 39 | + |
| 40 | +Explanation: |
| 41 | +We can partition the string s into 2 substrings like so: ("abab", "abaccddb"). |
| 42 | + |
| 43 | +**Constraints:** |
| 44 | + |
| 45 | + - `1 <= s.length <= 1000` |
| 46 | + - `s consists only of English lowercase letters.` |
| 47 | + |
| 48 | +## Solutions: |
| 49 | + |
| 50 | +### Intuition |
| 51 | + |
| 52 | +The problem requires partitioning a string such that each substring is balanced. A substring is balanced if each character appears with the same frequency within that substring. The challenge is to minimize the number of these substrings. This problem can be addressed by understanding the distribution of character frequencies and leveraging dynamic programming to minimize partitions while ensuring that each segment conforms to the balanced condition. |
| 53 | + |
| 54 | +### Approach |
| 55 | + |
| 56 | + - **Character Frequency Calculation:** |
| 57 | + - Calculate the maximum frequency for each character in the entire string. This will help determine if a substring can potentially be balanced, as every character in a balanced substring must appear at least as many times as its maximum frequency across the string. |
| 58 | + |
| 59 | + - **Dynamic Programming Initialization:** |
| 60 | + - Use a DP array dp where dp[i] represents the minimum number of partitions needed for the first i characters of the string. Initialize dp[0] to 0 because no partitions are needed for an empty string. |
| 61 | + |
| 62 | + - **Sliding Window for Substrings:** |
| 63 | + - For each position i in the string, consider every possible substring ending at i and starting from any j (where j ranges from 1 to i). This forms a sliding window from j to i. |
| 64 | + |
| 65 | + - For each window, maintain a frequency count of characters and determine the maximum frequency in the current window. |
| 66 | + |
| 67 | + - **Validation and DP Update:** |
| 68 | + - Validate each window by checking if all characters that appear have the frequency equal to the maximum frequency observed in that window. If the substring formed by the window is balanced, then update dp[i] to be the minimum of its current value or dp[j-1] + 1. |
| 69 | + |
| 70 | + - This ensures that dp[i] reflects the minimum partitions up to i including possibly ending a new balanced substring at i. |
| 71 | + |
| 72 | + - **Result Extraction:** |
| 73 | + - After processing all characters, dp[length of string] contains the minimum number of partitions for the entire string. |
| 74 | + |
| 75 | +## code: |
| 76 | + |
| 77 | +<Tabs> |
| 78 | + <TabItem value="cpp" label="C++" default> |
| 79 | + <SolutionAuthor name="@Ajay-Dhangar"/> |
| 80 | + ```cpp |
| 81 | + public: |
| 82 | + int minimumSubstringsInPartition(string s) { |
| 83 | + vector<int> maxFrequency(26, 0); |
| 84 | + for (char ch : s) { |
| 85 | + maxFrequency[ch - 'a']++; |
| 86 | + } |
| 87 | + |
| 88 | + vector<int> dp(s.length() + 1, INT_MAX); |
| 89 | + dp[0] = 0; // Base case: no characters, no partitions needed |
| 90 | + |
| 91 | + for (int i = 1; i <= s.length(); i++) { |
| 92 | + vector<int> freq(26, 0); |
| 93 | + int maxInWindow = 0; |
| 94 | + |
| 95 | + for (int j = i; j > 0; j--) { |
| 96 | + int index = s[j - 1] - 'a'; |
| 97 | + freq[index]++; |
| 98 | + maxInWindow = max(maxInWindow, freq[index]); |
| 99 | + |
| 100 | + bool valid = true; |
| 101 | + for (int k = 0; k < 26; k++) { |
| 102 | + if (freq[k] > 0 && freq[k] < maxInWindow) { |
| 103 | + valid = false; |
| 104 | + break; |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + if (valid) { |
| 109 | + dp[i] = min(dp[i], dp[j - 1] + 1); |
| 110 | + } |
| 111 | + } |
| 112 | + } |
| 113 | + return dp[s.length()]; |
| 114 | + } |
| 115 | + ``` |
| 116 | + </TabItem> |
| 117 | + <TabItem value="java" label="Java"> |
| 118 | + <SolutionAuthor name="@Ajay-Dhangar"/> |
| 119 | + ```java |
| 120 | + public int minimumSubstringsInPartition(String s) { |
| 121 | + // Initialize an array to store the maximum frequency of each character |
| 122 | + int[] maxFrequency = new int[26]; // Array to hold maximum frequency of each character 'a' to 'z' |
| 123 | + for (int i = 0; i < s.length(); i++) { |
| 124 | + maxFrequency[s.charAt(i) - 'a']++; // Increment frequency of character at position i |
| 125 | + } |
| 126 | + |
| 127 | + // dp[i] represents the minimum number of partitions needed for the first i characters |
| 128 | + int[] dp = new int[s.length() + 1]; // Array to hold the minimum partitions needed for each prefix of s |
| 129 | + dp[0] = 0; // Base case: no characters, no partitions needed |
| 130 | + |
| 131 | + for (int i = 1; i <= s.length(); i++) { |
| 132 | + dp[i] = Integer.MAX_VALUE; // Initialize dp[i] with a large number |
| 133 | + int[] freq = new int[26]; // Array to keep track of character frequencies in the current window |
| 134 | + int maxInWindow = 0; // Variable to store the maximum frequency in the current window |
| 135 | + |
| 136 | + // Check all substrings that end at position i |
| 137 | + for (int j = i; j > 0; j--) { |
| 138 | + char c = s.charAt(j - 1); // Get character in the string at position j-1 |
| 139 | + freq[c - 'a']++; // Increment frequency of that character in the current window |
| 140 | + maxInWindow = Math.max(maxInWindow, freq[c - 'a']); // Update maxInWindow if current frequency is higher |
| 141 | + |
| 142 | + // Check if the current window can form a balanced substring |
| 143 | + boolean isValid = true; // Flag to check if the window is valid |
| 144 | + for (int k = 0; k < 26; k++) { |
| 145 | + if (freq[k] > 0 && freq[k] < maxInWindow) { |
| 146 | + isValid = false; // Set valid to false if any character frequency is less than maxInWindow |
| 147 | + break; // Exit the loop as the substring is not valid |
| 148 | + } |
| 149 | + } |
| 150 | + |
| 151 | + if (isValid) { // If the window is valid |
| 152 | + dp[i] = Math.min(dp[i], dp[j - 1] + 1); // Update dp[i] with the minimum partitions found |
| 153 | + } |
| 154 | + } |
| 155 | + } |
| 156 | + return dp[s.length()]; // Return the minimum number of partitions for the entire string |
| 157 | + } |
| 158 | + ``` |
| 159 | + </TabItem> |
| 160 | + <TabItem value="python" label="Python"> |
| 161 | + <SolutionAuthor name="@Ajay-Dhangar"/> |
| 162 | + ```python |
| 163 | + def minimumSubstringsInPartition(self, s): |
| 164 | + # Initialize an array to store the maximum frequency of each character |
| 165 | + max_frequency = [0] * 26 |
| 166 | + for char in s: |
| 167 | + max_frequency[ord(char) - ord('a')] += 1 |
| 168 | + |
| 169 | + # dp[i] represents the minimum number of partitions needed for the first i characters |
| 170 | + dp = [float('inf')] * (len(s) + 1) |
| 171 | + dp[0] = 0 # Base case: no characters, no partitions needed |
| 172 | + |
| 173 | + for i in range(1, len(s) + 1): |
| 174 | + freq = [0] * 26 |
| 175 | + max_in_window = 0 |
| 176 | + |
| 177 | + # Check all substrings that end at position i |
| 178 | + for j in range(i, 0, -1): |
| 179 | + index = ord(s[j - 1]) - ord('a') |
| 180 | + freq[index] += 1 |
| 181 | + max_in_window = max(max_in_window, freq[index]) |
| 182 | + |
| 183 | + # Check if the current window can form a balanced substring |
| 184 | + valid = True |
| 185 | + for k in range(26): |
| 186 | + if freq[k] > 0 and freq[k] < max_in_window: |
| 187 | + valid = False |
| 188 | + break |
| 189 | + |
| 190 | + if valid: |
| 191 | + dp[i] = min(dp[i], dp[j - 1] + 1) |
| 192 | + |
| 193 | + return dp[len(s)] |
| 194 | + ``` |
| 195 | + </TabItem> |
| 196 | + <TabItem value="c" label="C"> |
| 197 | + <SolutionAuthor name="@Ajay-Dhangar"/> |
| 198 | + ```c |
| 199 | + int minimumSubstringsInPartition(char* s) { |
| 200 | + int len = strlen(s); |
| 201 | + int maxFrequency[26] = {0}, dp[len + 1], freq[26], i, j, k, maxInWindow; |
| 202 | + for (i = 0; s[i]; i++) { |
| 203 | + maxFrequency[s[i] - 'a']++; |
| 204 | + } |
| 205 | + |
| 206 | + dp[0] = 0; // Base case |
| 207 | + for (i = 1; i <= len; i++) { |
| 208 | + dp[i] = INT_MAX; |
| 209 | + memset(freq, 0, sizeof(freq)); |
| 210 | + maxInWindow = 0; |
| 211 | + |
| 212 | + for (j = i; j > 0; j--) { |
| 213 | + char c = s[j - 1]; |
| 214 | + freq[c - 'a']++; |
| 215 | + if (freq[c - 'a'] > maxInWindow) maxInWindow = freq[c - 'a']; |
| 216 | + |
| 217 | + int valid = 1; |
| 218 | + for (k = 0; k < 26; k++) { |
| 219 | + if (freq[k] > 0 && freq[k] < maxInWindow) { |
| 220 | + valid = 0; |
| 221 | + break; |
| 222 | + } |
| 223 | + } |
| 224 | + |
| 225 | + if (valid) { |
| 226 | + if (dp[j - 1] + 1 < dp[i]) dp[i] = dp[j - 1] + 1; |
| 227 | + } |
| 228 | + } |
| 229 | + } |
| 230 | + |
| 231 | + return dp[len]; |
| 232 | + |
| 233 | + } |
| 234 | + ``` |
| 235 | + |
| 236 | +</TabItem> |
| 237 | + |
| 238 | +</Tabs> |
| 239 | + |
| 240 | +## Complexity |
| 241 | + |
| 242 | +**Time complexity:** $O(n^3)$ - The solution involves three nested loops: The outermost loop runs for each character i in the string. For each i, the middle loop considers every possible starting point j for substrings ending at i, and the innermost loop iterates through all 26 characters to ensure the substring from j to i is balanced. |
| 243 | + |
| 244 | + -This results in a time complexity proportional to `n^2` multiplied by a constant factor (26), simplifying to $O(n^3)$. |
| 245 | + |
| 246 | +**Space complexity:** $O(n + 26)$ |
| 247 | + |
| 248 | + - $O(n)$ for the dynamic programming array dp which stores the minimum partitions for each substring ending at each position. |
| 249 | + |
| 250 | + - $O(26)$ (constant space) for the frequency array used in each sliding window to keep track of character counts. This part is constant since the number of characters (English lowercase letters) does not scale with n. |
| 251 | + |
| 252 | +This algorithm efficiently handles smaller strings and provides a clear method to determine minimal partitions, but it might be computationally intensive for very large strings due to its cubic time complexity. |
0 commit comments