这题就是先统计字符串里每个字符出现的频率,然后用一个set保存不重复的频率,遍历每个频率,不在set里直接累加,在的话就一直减直到不在set里,此时频率大于0则累加。最后答案等于字符串长度减掉累加的频率数。
class Solution {
public int minDeletions(String s) {
Map<Character, Integer> freq = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
int cnt = freq.getOrDefault(s.charAt(i), 0);
freq.put(s.charAt(i), cnt + 1);
}
boolean[] visited = new boolean[100001];
int sum = 0;
for (Map.Entry<Character, Integer> e : freq.entrySet()) {
int index = e.getValue();
while (index > 0 && visited[index]) {
index--;
}
if (index > 0) {
visited[index] = true;
sum += index;
}
}
return s.length() - sum;
}
}
上面是我第一版的代码,写完之后提交发现很慢,然后回头看觉得蠢的不行,我明明只用到了每个字符的频率,那根本不需要用map,因为a-z就26个字符嘛,直接用长度26的int[]就行了。然后我这个10^5+1的数组也是开的令人窒息,直接用一个set保存频率就行了。
优化之后的代码如下。
class Solution {
public int minDeletions(String s) {
int[] freq = new int[26];
for (int i = 0; i < s.length(); i++) {
freq[s.charAt(i) - 'a']++;
}
Set<Integer> uniqueFreq = new HashSet<>();
int sum = 0;
Arrays.sort(freq);
for (int f : freq) {
while (uniqueFreq.contains(f) && f > 0) {
f--;
}
if (f > 0) {
sum += f;
uniqueFreq.add(f);
}
}
return s.length() - sum;
}
}
后来我又试了一下,这里对于长为26的频率数组是否排序完全不影响结果,因为如果有多个相同频率的字符,对于结果没有影响,结果只是问长度,而不是问具体删减之后的字符串长啥样,这有点像之前的找最大公共子串,只是问长度,没问具体子串是啥,当然这个我会,后面记得填坑。
回过来,虽然排序不影响结果,但是我一开始写的时候对于怎么给int[]数组降序排序折腾了半天,Arrays.sort(int[]) 后面没法传入Collections.reverseOrder(),所以要么直接sort之后反过来,要么用Integer[]数组之后用Arrays.sort(a, Collections.reverseOrder()),注意这样的话要循环初始化每一位,不然每一位是空指针。