Welcome to Subscribe On Youtube

3144. Minimum Substring Partition of Equal Character Frequency

Description

Given a string s, you need to partition it into one or more balanced substrings. 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.

Return the minimum number of substrings that you can partition s into.

Note: A balanced string is a string where each character in the string occurs the same number of times.

 

Example 1:

Input: s = "fabccddg"

Output: 3

Explanation:

We can partition the string s into 3 substrings in one of the following ways: ("fab, "ccdd", "g"), or ("fabc", "cd", "dg").

Example 2:

Input: s = "abababaccddb"

Output: 2

Explanation:

We can partition the string s into 2 substrings like so: ("abab", "abaccddb").

 

Constraints:

  • 1 <= s.length <= 1000
  • s consists only of English lowercase letters.

Solutions

Solution 1: Memoization Search + Hash Table

We design a function $\text{dfs}(i)$, which represents the minimum number of substrings starting from the string $s[i]$. So the answer is $\text{dfs}(0)$.

The calculation process of the function $\text{dfs}(i)$ is as follows:

If $i \geq n$, it means that all characters have been processed, return $0$.

Otherwise, we maintain a hash table $\text{cnt}$, which represents the number of occurrences of each character in the current substring. In addition, we also maintain a hash table $\text{freq}$, which represents the frequency of the number of occurrences of each character.

Then we enumerate $j$ from $i$ to $n-1$, which represents the end position of the current substring. For each $j$, we update $\text{cnt}$ and $\text{freq}$, then check whether the size of $\text{freq}$ is $1$. If so, we can start splitting from $j+1$, at this time the answer is $1 + \text{dfs}(j+1)$. We take the minimum value of the answer among all $j$ as the return value of the function.

To avoid repeated calculations, we use memoization search.

The time complexity is $O(n^2)$, and the space complexity is $O(n)$. Where $n$ is the length of the string $s$.

Solution 2: Memoized Search (Optimization)

We can optimize Solution 1 by not maintaining the $\textit{freq}$ hash table. Instead, we only need to maintain a hash table $\textit{cnt}$, which represents the frequency of each character in the current substring. Additionally, we maintain two variables $k$ and $m$ to represent the number of distinct characters in the current substring and the maximum frequency of any character, respectively. For a substring $s[i..j]$, if $j-i+1 = m \times k$, then this substring is a balanced substring.

The time complexity is $O(n^2)$, and the space complexity is $O(n \times \Sigma )$. Here, $n$ is the length of the string $s$, and $ \Sigma $ represents the size of the character set, which is $26$ in this problem.

Solution 3: Dynamic Programming

We can convert the memoized search into dynamic programming. Define the state $f[i]$ as the minimum number of substrings required to partition the first $i$ characters. Initially, $f[0] = 0$, and the rest $f[i] = +\infty$ or $f[i] = n$.

Next, we enumerate $i$ from $0$ to $n-1$. For each $i$, we maintain a hash table $\textit{cnt}$ to represent the frequency of each character in the current substring. Additionally, we maintain two variables $k$ and $m$ to represent the number of distinct characters in the current substring and the maximum frequency of any character, respectively. For a substring $s[j..i]$, if $i-j+1 = m \times k$, then this substring is a balanced substring. At this point, we can partition from $j$, so $f[i+1] = \min(f[i+1], f[j] + 1)$.

The final answer is $f[n]$.

The time complexity is $O(n^2)$, and the space complexity is $O(n + \Sigma )$. Here, $n$ is the length of the string $s$, and $ \Sigma $ represents the size of the character set, which is $26$ in this problem.

Solution 4: Dynamic Programming + Hash Table

Thinking

Enumerating every partition is exponential. The string length reaches $1000$, and a single character is already a balanced piece, so the first transition always moves to the next index and the search has depth $n$. Whether a piece is balanced depends only on the frequencies inside it, so the answer from index $i$ depends only on later answers. Let $f[i]$ be the fewest pieces starting at $i$, with $f[n]=0$, and scan right endpoints from the end, updating with $1+f[j+1]$ when $freq$ contains only one frequency.

Let $f[i]$ be the minimum number of substrings starting at index $i$, with $f[n]=0$. For $i$ from $n-1$ down to $0$, keep a hash table $\textit{cnt}$ of character frequencies in the current piece and a hash table $\textit{freq}$ of how often each frequency occurs. Enumerate the right endpoint $j$ from $i$ to $n-1$, append $s[j]$, and update both tables. If $\textit{freq}$ has size $1$, then $s[i..j]$ is balanced and $f[i]$ is updated with $1+f[j+1]$. Every character can stand alone, so $n-i$ is a valid upper bound. The answer is $f[0]$.

The time complexity is $O(n^2)$, and the space complexity is $O(n + \Sigma )$. Here, $n$ is the length of the string $s$, and $ \Sigma $ represents the size of the character set, which is $26$ in this problem.

Solution 5: Dynamic Programming (Optimization)

Thinking

Solution 1 deletes the old frequency and inserts the new one on every appended character. Balance is the same as the piece length equaling the maximum frequency times the number of distinct letters, so one $\textit{cnt}$ is enough. The answer at index $i$ still depends only on the right, so $f[i]$ is filled from the end. While extending $j$, track the number of distinct letters $k$ and the maximum frequency $m$, and update with $1+f[j+1]$ when $j-i+1=k\cdot m$.

The $\textit{freq}$ table in Solution 1 can be dropped. Keep only the character counts $\textit{cnt}$, the number of distinct letters $k$, and the maximum frequency $m$. $f[i]$ is still the fewest pieces starting at index $i$, with $f[n]=0$. Scan the start index from the right and extend $j$ to the right. If $j-i+1 = m \times k$, then $s[i..j]$ is balanced and $f[i]$ is updated with $1+f[j+1]$. The answer is $f[0]$.

The time complexity is $O(n^2)$, and the space complexity is $O(n + \Sigma )$. Here, $n$ is the length of the string $s$, and $ \Sigma $ represents the size of the character set, which is $26$ in this problem.
  • class Solution {
        private int n;
        private char[] s;
        private Integer[] f;
    
        public int minimumSubstringsInPartition(String s) {
            n = s.length();
            f = new Integer[n];
            this.s = s.toCharArray();
            return dfs(0);
        }
    
        private int dfs(int i) {
            if (i >= n) {
                return 0;
            }
            if (f[i] != null) {
                return f[i];
            }
            int[] cnt = new int[26];
            Map<Integer, Integer> freq = new HashMap<>(26);
            int ans = n - i;
            for (int j = i; j < n; ++j) {
                int k = s[j] - 'a';
                if (cnt[k] > 0) {
                    if (freq.merge(cnt[k], -1, Integer::sum) == 0) {
                        freq.remove(cnt[k]);
                    }
                }
                ++cnt[k];
                freq.merge(cnt[k], 1, Integer::sum);
                if (freq.size() == 1) {
                    ans = Math.min(ans, 1 + dfs(j + 1));
                }
            }
            return f[i] = ans;
        }
    }
    
    
    // Solution 2
    class Solution {
        private int n;
        private char[] s;
        private Integer[] f;
    
        public int minimumSubstringsInPartition(String s) {
            n = s.length();
            f = new Integer[n];
            this.s = s.toCharArray();
            return dfs(0);
        }
    
        private int dfs(int i) {
            if (i >= n) {
                return 0;
            }
            if (f[i] != null) {
                return f[i];
            }
            int[] cnt = new int[26];
            int ans = n - i;
            int k = 0, m = 0;
            for (int j = i; j < n; ++j) {
                k += ++cnt[s[j] - 'a'] == 1 ? 1 : 0;
                m = Math.max(m, cnt[s[j] - 'a']);
                if (j - i + 1 == k * m) {
                    ans = Math.min(ans, 1 + dfs(j + 1));
                }
            }
            return f[i] = ans;
        }
    }
    
    
    
    // Solution 3
    class Solution {
        public int minimumSubstringsInPartition(String s) {
            int n = s.length();
            char[] cs = s.toCharArray();
            int[] f = new int[n + 1];
            Arrays.fill(f, n);
            f[0] = 0;
            for (int i = 0; i < n; ++i) {
                int[] cnt = new int[26];
                int k = 0, m = 0;
                for (int j = i; j >= 0; --j) {
                    k += ++cnt[cs[j] - 'a'] == 1 ? 1 : 0;
                    m = Math.max(m, cnt[cs[j] - 'a']);
                    if (i - j + 1 == k * m) {
                        f[i + 1] = Math.min(f[i + 1], 1 + f[j]);
                    }
                }
            }
            return f[n];
        }
    }
    
    
    
    // Solution 4
    class Solution {
        public int minimumSubstringsInPartition(String s) {
            int n = s.length();
            char[] cs = s.toCharArray();
            int[] f = new int[n + 1];
            for (int i = n - 1; i >= 0; --i) {
                int[] cnt = new int[26];
                Map<Integer, Integer> freq = new HashMap<>(26);
                int ans = n - i;
                for (int j = i; j < n; ++j) {
                    int k = cs[j] - 'a';
                    if (cnt[k] > 0) {
                        if (freq.merge(cnt[k], -1, Integer::sum) == 0) {
                            freq.remove(cnt[k]);
                        }
                    }
                    ++cnt[k];
                    freq.merge(cnt[k], 1, Integer::sum);
                    if (freq.size() == 1) {
                        ans = Math.min(ans, 1 + f[j + 1]);
                    }
                }
                f[i] = ans;
            }
            return f[0];
        }
    }
    
    
    
    // Solution 5
    class Solution {
        public int minimumSubstringsInPartition(String s) {
            int n = s.length();
            char[] cs = s.toCharArray();
            int[] f = new int[n + 1];
            for (int i = n - 1; i >= 0; --i) {
                int[] cnt = new int[26];
                int ans = n - i;
                int k = 0, m = 0;
                for (int j = i; j < n; ++j) {
                    k += ++cnt[cs[j] - 'a'] == 1 ? 1 : 0;
                    m = Math.max(m, cnt[cs[j] - 'a']);
                    if (j - i + 1 == k * m) {
                        ans = Math.min(ans, 1 + f[j + 1]);
                    }
                }
                f[i] = ans;
            }
            return f[0];
        }
    }
    
    
  • class Solution {
    public:
        int minimumSubstringsInPartition(string s) {
            int n = s.size();
            int f[n];
            memset(f, -1, sizeof(f));
            function<int(int)> dfs = [&](int i) {
                if (i >= n) {
                    return 0;
                }
                if (f[i] != -1) {
                    return f[i];
                }
                f[i] = n - i;
                int cnt[26]{};
                unordered_map<int, int> freq;
                for (int j = i; j < n; ++j) {
                    int k = s[j] - 'a';
                    if (cnt[k]) {
                        freq[cnt[k]]--;
                        if (freq[cnt[k]] == 0) {
                            freq.erase(cnt[k]);
                        }
                    }
                    ++cnt[k];
                    ++freq[cnt[k]];
                    if (freq.size() == 1) {
                        f[i] = min(f[i], 1 + dfs(j + 1));
                    }
                }
                return f[i];
            };
            return dfs(0);
        }
    };
    
    
    // Solution 2
    class Solution {
    public:
        int minimumSubstringsInPartition(string s) {
            int n = s.size();
            int f[n];
            memset(f, -1, sizeof(f));
            auto dfs = [&](this auto&& dfs, int i) -> int {
                if (i >= n) {
                    return 0;
                }
                if (f[i] != -1) {
                    return f[i];
                }
                f[i] = n - i;
                int cnt[26]{};
                int k = 0, m = 0;
                for (int j = i; j < n; ++j) {
                    k += ++cnt[s[j] - 'a'] == 1 ? 1 : 0;
                    m = max(m, cnt[s[j] - 'a']);
                    if (j - i + 1 == k * m) {
                        f[i] = min(f[i], 1 + dfs(j + 1));
                    }
                }
                return f[i];
            };
            return dfs(0);
        }
    };
    
    
    
    // Solution 3
    class Solution {
    public:
        int minimumSubstringsInPartition(string s) {
            int n = s.size();
            vector<int> f(n + 1, n);
            f[0] = 0;
            for (int i = 0; i < n; ++i) {
                int cnt[26]{};
                int k = 0, m = 0;
                for (int j = i; ~j; --j) {
                    k += ++cnt[s[j] - 'a'] == 1;
                    m = max(m, cnt[s[j] - 'a']);
                    if (i - j + 1 == k * m) {
                        f[i + 1] = min(f[i + 1], f[j] + 1);
                    }
                }
            }
            return f[n];
        }
    };
    
    
    
    // Solution 4
    class Solution {
    public:
        int minimumSubstringsInPartition(string s) {
            int n = s.size();
            vector<int> f(n + 1);
            for (int i = n - 1; i >= 0; --i) {
                int cnt[26]{};
                unordered_map<int, int> freq;
                int ans = n - i;
                for (int j = i; j < n; ++j) {
                    int k = s[j] - 'a';
                    if (cnt[k]) {
                        freq[cnt[k]]--;
                        if (freq[cnt[k]] == 0) {
                            freq.erase(cnt[k]);
                        }
                    }
                    ++cnt[k];
                    ++freq[cnt[k]];
                    if (freq.size() == 1) {
                        ans = min(ans, 1 + f[j + 1]);
                    }
                }
                f[i] = ans;
            }
            return f[0];
        }
    };
    
    
    
    // Solution 5
    class Solution {
    public:
        int minimumSubstringsInPartition(string s) {
            int n = s.size();
            vector<int> f(n + 1);
            for (int i = n - 1; i >= 0; --i) {
                int cnt[26]{};
                int ans = n - i;
                int k = 0, m = 0;
                for (int j = i; j < n; ++j) {
                    k += ++cnt[s[j] - 'a'] == 1 ? 1 : 0;
                    m = max(m, cnt[s[j] - 'a']);
                    if (j - i + 1 == k * m) {
                        ans = min(ans, 1 + f[j + 1]);
                    }
                }
                f[i] = ans;
            }
            return f[0];
        }
    };
    
    
  • class Solution:
        def minimumSubstringsInPartition(self, s: str) -> int:
            @cache
            def dfs(i: int) -> int:
                if i >= n:
                    return 0
                cnt = defaultdict(int)
                freq = defaultdict(int)
                ans = n - i
                for j in range(i, n):
                    if cnt[s[j]]:
                        freq[cnt[s[j]]] -= 1
                        if not freq[cnt[s[j]]]:
                            freq.pop(cnt[s[j]])
                    cnt[s[j]] += 1
                    freq[cnt[s[j]]] += 1
                    if len(freq) == 1 and (t := 1 + dfs(j + 1)) < ans:
                        ans = t
                return ans
    
            n = len(s)
            return dfs(0)
    
    
    # Solution 2
    class Solution:
        def minimumSubstringsInPartition(self, s: str) -> int:
            @cache
            def dfs(i: int) -> int:
                if i >= n:
                    return 0
                cnt = defaultdict(int)
                m = 0
                ans = n - i
                for j in range(i, n):
                    cnt[s[j]] += 1
                    m = max(m, cnt[s[j]])
                    if j - i + 1 == m * len(cnt):
                        ans = min(ans, 1 + dfs(j + 1))
                return ans
    
            n = len(s)
            ans = dfs(0)
            dfs.cache_clear()
            return ans
    
    
    
    # Solution 3
    class Solution:
        def minimumSubstringsInPartition(self, s: str) -> int:
            n = len(s)
            f = [inf] * (n + 1)
            f[0] = 0
            for i in range(n):
                cnt = defaultdict(int)
                m = 0
                for j in range(i, -1, -1):
                    cnt[s[j]] += 1
                    m = max(m, cnt[s[j]])
                    if i - j + 1 == len(cnt) * m:
                        f[i + 1] = min(f[i + 1], f[j] + 1)
            return f[n]
    
    
    
    # Solution 4
    class Solution:
        def minimumSubstringsInPartition(self, s: str) -> int:
            n = len(s)
            f = [0] * (n + 1)
            for i in range(n - 1, -1, -1):
                cnt = defaultdict(int)
                freq = defaultdict(int)
                ans = n - i
                for j in range(i, n):
                    if cnt[s[j]]:
                        freq[cnt[s[j]]] -= 1
                        if not freq[cnt[s[j]]]:
                            freq.pop(cnt[s[j]])
                    cnt[s[j]] += 1
                    freq[cnt[s[j]]] += 1
                    if len(freq) == 1:
                        ans = min(ans, 1 + f[j + 1])
                f[i] = ans
            return f[0]
    
    
    
    # Solution 5
    class Solution:
        def minimumSubstringsInPartition(self, s: str) -> int:
            n = len(s)
            f = [0] * (n + 1)
            for i in range(n - 1, -1, -1):
                cnt = defaultdict(int)
                m = 0
                ans = n - i
                for j in range(i, n):
                    cnt[s[j]] += 1
                    m = max(m, cnt[s[j]])
                    if j - i + 1 == m * len(cnt):
                        ans = min(ans, 1 + f[j + 1])
                f[i] = ans
            return f[0]
    
    
  • func minimumSubstringsInPartition(s string) int {
    	n := len(s)
    	f := make([]int, n)
    	for i := range f {
    		f[i] = -1
    	}
    	var dfs func(int) int
    	dfs = func(i int) int {
    		if i >= n {
    			return 0
    		}
    		if f[i] != -1 {
    			return f[i]
    		}
    		cnt := [26]int{}
    		freq := map[int]int{}
    		f[i] = n - i
    		for j := i; j < n; j++ {
    			k := int(s[j] - 'a')
    			if cnt[k] > 0 {
    				freq[cnt[k]]--
    				if freq[cnt[k]] == 0 {
    					delete(freq, cnt[k])
    				}
    			}
    			cnt[k]++
    			freq[cnt[k]]++
    			if len(freq) == 1 {
    				f[i] = min(f[i], 1+dfs(j+1))
    			}
    		}
    		return f[i]
    	}
    	return dfs(0)
    }
    
    
    // Solution 2
    func minimumSubstringsInPartition(s string) int {
    	n := len(s)
    	f := make([]int, n)
    	for i := range f {
    		f[i] = -1
    	}
    	var dfs func(int) int
    	dfs = func(i int) int {
    		if i >= n {
    			return 0
    		}
    		if f[i] != -1 {
    			return f[i]
    		}
    		cnt := [26]int{}
    		f[i] = n - i
    		k, m := 0, 0
    		for j := i; j < n; j++ {
    			x := int(s[j] - 'a')
    			cnt[x]++
    			if cnt[x] == 1 {
    				k++
    			}
    			m = max(m, cnt[x])
    			if j-i+1 == k*m {
    				f[i] = min(f[i], 1+dfs(j+1))
    			}
    		}
    		return f[i]
    	}
    	return dfs(0)
    }
    
    
    
    // Solution 3
    func minimumSubstringsInPartition(s string) int {
    	n := len(s)
    	f := make([]int, n+1)
    	for i := range f {
    		f[i] = n
    	}
    	f[0] = 0
    	for i := 0; i < n; i++ {
    		cnt := [26]int{}
    		k, m := 0, 0
    		for j := i; j >= 0; j-- {
    			x := int(s[j] - 'a')
    			cnt[x]++
    			if cnt[x] == 1 {
    				k++
    			}
    			m = max(m, cnt[x])
    			if i-j+1 == k*m {
    				f[i+1] = min(f[i+1], 1+f[j])
    			}
    		}
    	}
    	return f[n]
    }
    
    
    
    // Solution 4
    func minimumSubstringsInPartition(s string) int {
    	n := len(s)
    	f := make([]int, n+1)
    	for i := n - 1; i >= 0; i-- {
    		cnt := [26]int{}
    		freq := map[int]int{}
    		ans := n - i
    		for j := i; j < n; j++ {
    			k := int(s[j] - 'a')
    			if cnt[k] > 0 {
    				freq[cnt[k]]--
    				if freq[cnt[k]] == 0 {
    					delete(freq, cnt[k])
    				}
    			}
    			cnt[k]++
    			freq[cnt[k]]++
    			if len(freq) == 1 {
    				ans = min(ans, 1+f[j+1])
    			}
    		}
    		f[i] = ans
    	}
    	return f[0]
    }
    
    
    
    // Solution 5
    func minimumSubstringsInPartition(s string) int {
    	n := len(s)
    	f := make([]int, n+1)
    	for i := n - 1; i >= 0; i-- {
    		cnt := [26]int{}
    		ans := n - i
    		k, m := 0, 0
    		for j := i; j < n; j++ {
    			x := int(s[j] - 'a')
    			cnt[x]++
    			if cnt[x] == 1 {
    				k++
    			}
    			m = max(m, cnt[x])
    			if j-i+1 == k*m {
    				ans = min(ans, 1+f[j+1])
    			}
    		}
    		f[i] = ans
    	}
    	return f[0]
    }
    
    
  • function minimumSubstringsInPartition(s: string): number {
        const n = s.length;
        const f: number[] = Array(n).fill(-1);
        const dfs = (i: number): number => {
            if (i >= n) {
                return 0;
            }
            if (f[i] !== -1) {
                return f[i];
            }
            const cnt: Map<number, number> = new Map();
            const freq: Map<number, number> = new Map();
            f[i] = n - i;
            for (let j = i; j < n; ++j) {
                const k = s.charCodeAt(j) - 97;
                if (freq.has(cnt.get(k)!)) {
                    freq.set(cnt.get(k)!, freq.get(cnt.get(k)!)! - 1);
                    if (freq.get(cnt.get(k)!) === 0) {
                        freq.delete(cnt.get(k)!);
                    }
                }
                cnt.set(k, (cnt.get(k) || 0) + 1);
                freq.set(cnt.get(k)!, (freq.get(cnt.get(k)!) || 0) + 1);
                if (freq.size === 1) {
                    f[i] = Math.min(f[i], 1 + dfs(j + 1));
                }
            }
            return f[i];
        };
        return dfs(0);
    }
    
    
    // Solution 2
    function minimumSubstringsInPartition(s: string): number {
        const n = s.length;
        const f: number[] = Array(n).fill(-1);
        const dfs = (i: number): number => {
            if (i >= n) {
                return 0;
            }
            if (f[i] !== -1) {
                return f[i];
            }
            const cnt: number[] = Array(26).fill(0);
            f[i] = n - i;
            let [k, m] = [0, 0];
            for (let j = i; j < n; ++j) {
                const x = s.charCodeAt(j) - 97;
                k += ++cnt[x] === 1 ? 1 : 0;
                m = Math.max(m, cnt[x]);
                if (j - i + 1 === k * m) {
                    f[i] = Math.min(f[i], 1 + dfs(j + 1));
                }
            }
            return f[i];
        };
        return dfs(0);
    }
    
    
    
    // Solution 3
    function minimumSubstringsInPartition(s: string): number {
        const n = s.length;
        const f: number[] = Array(n + 1).fill(n);
        f[0] = 0;
        for (let i = 0; i < n; ++i) {
            const cnt: number[] = Array(26).fill(0);
            let [k, m] = [0, 0];
            for (let j = i; ~j; --j) {
                const x = s.charCodeAt(j) - 97;
                k += ++cnt[x] === 1 ? 1 : 0;
                m = Math.max(m, cnt[x]);
                if (i - j + 1 === k * m) {
                    f[i + 1] = Math.min(f[i + 1], 1 + f[j]);
                }
            }
        }
        return f[n];
    }
    
    
    
    // Solution 4
    function minimumSubstringsInPartition(s: string): number {
        const n = s.length;
        const f: number[] = Array(n + 1).fill(0);
        for (let i = n - 1; i >= 0; --i) {
            const cnt: Map<number, number> = new Map();
            const freq: Map<number, number> = new Map();
            let ans = n - i;
            for (let j = i; j < n; ++j) {
                const k = s.charCodeAt(j) - 97;
                if (freq.has(cnt.get(k)!)) {
                    freq.set(cnt.get(k)!, freq.get(cnt.get(k)!)! - 1);
                    if (freq.get(cnt.get(k)!) === 0) {
                        freq.delete(cnt.get(k)!);
                    }
                }
                cnt.set(k, (cnt.get(k) || 0) + 1);
                freq.set(cnt.get(k)!, (freq.get(cnt.get(k)!) || 0) + 1);
                if (freq.size === 1) {
                    ans = Math.min(ans, 1 + f[j + 1]);
                }
            }
            f[i] = ans;
        }
        return f[0];
    }
    
    
    
    // Solution 5
    function minimumSubstringsInPartition(s: string): number {
        const n = s.length;
        const f: number[] = Array(n + 1).fill(0);
        for (let i = n - 1; i >= 0; --i) {
            const cnt: number[] = Array(26).fill(0);
            let ans = n - i;
            let [k, m] = [0, 0];
            for (let j = i; j < n; ++j) {
                const x = s.charCodeAt(j) - 97;
                k += ++cnt[x] === 1 ? 1 : 0;
                m = Math.max(m, cnt[x]);
                if (j - i + 1 === k * m) {
                    ans = Math.min(ans, 1 + f[j + 1]);
                }
            }
            f[i] = ans;
        }
        return f[0];
    }
    
    

All Problems

All Solutions