Easy
题目描述
给你一个下标从 0 开始的字符串 word,由小写英文字母组成。你需要选择一个下标并从 word 中删除该下标处的字母,使得 word 中每个字母的频次都相等。
如果删除一个字母后可以使 word 中所有字母的频次都相等,返回 true;否则,返回 false。
注意:
- 字母
x的频次是指它在字符串中出现的次数。 - 你必须删除恰好一个字母,不能选择不删除。
示例 1:
输入:word = "abcc"
输出:true
解释:选择下标 3 并删除它:word 变为 "abc",每个字符的频次都为 1。
示例 2:
输入:word = "aazz"
输出:false
解释:我们必须删除一个字符,这样要么 "a" 的频次为 1 而 "z" 的频次为 2,要么相反。不可能使所有出现的字母频次相等。
约束条件:
2 <= word.length <= 100word只包含小写英文字母
提示:
- 暴力枚举所有可能被删除的字母。
- 使用大小为 26 的频次数组。
解题思路
这道题要求我们删除恰好一个字母后,使得剩余字母的频次都相等。我们可以采用暴力枚举的方法:
核心思路:
- 枚举删除每个位置的字母,检查删除后是否满足条件
- 对于每次删除操作,统计剩余字母的频次
- 检查所有非零频次是否相等
具体步骤:
- 遍历字符串的每个位置 i,假设删除位置 i 的字母
- 统计删除该字母后各字母的频次
- 收集所有非零频次,检查是否都相等
- 如果找到一个删除位置使得频次都相等,返回 true
- 如果所有位置都尝试过仍不满足,返回 false
时间复杂度分析:
- 外层循环遍历字符串:O(n)
- 内层统计频次:O(n)
- 检查频次是否相等:O(26) = O(1)
- 总体时间复杂度:O(n²)
由于字符串长度最大为 100,这个复杂度完全可以接受。
优化思考: 虽然可以先统计原始频次,然后枚举删除每个字母来优化,但考虑到数据规模较小,暴力解法更加简洁易懂。
代码实现
class Solution {
public:
bool equalFrequency(string word) {
int n = word.length();
// Try removing each character
for (int i = 0; i < n; i++) {
vector<int> freq(26, 0);
// Count frequency after removing character at index i
for (int j = 0; j < n; j++) {
if (i != j) {
freq[word[j] - 'a']++;
}
}
// Collect all non-zero frequencies
vector<int> nonZeroFreqs;
for (int f : freq) {
if (f > 0) {
nonZeroFreqs.push_back(f);
}
}
// Check if all non-zero frequencies are equal
if (!nonZeroFreqs.empty()) {
bool allEqual = true;
for (int f : nonZeroFreqs) {
if (f != nonZeroFreqs[0]) {
allEqual = false;
break;
}
}
if (allEqual) return true;
}
}
return false;
}
};
class Solution:
def equalFrequency(self, word: str) -> bool:
n = len(word)
# Try removing each character
for i in range(n):
freq = [0] * 26
# Count frequency after removing character at index i
for j in range(n):
if i != j:
freq[ord(word[j]) - ord('a')] += 1
# Collect all non-zero frequencies
non_zero_freqs = [f for f in freq if f > 0]
# Check if all non-zero frequencies are equal
if non_zero_freqs and all(f == non_zero_freqs[0] for f in non_zero_freqs):
return True
return False
public class Solution {
public bool EqualFrequency(string word) {
int n = word.Length;
// Try removing each character
for (int i = 0; i < n; i++) {
int[] freq = new int[26];
// Count frequency after removing character at index i
for (int j = 0; j < n; j++) {
if (i != j) {
freq[word[j] - 'a']++;
}
}
// Collect all non-zero frequencies
List<int> nonZeroFreqs = new List<int>();
for (int f = 0; f < 26; f++) {
if (freq[f] > 0) {
nonZeroFreqs.Add(freq[f]);
}
}
// Check if all non-zero frequencies are equal
if (nonZeroFreqs.Count > 0) {
bool allEqual = true;
for (int k = 0; k < nonZeroFreqs.Count; k++) {
if (nonZeroFreqs[k] != nonZeroFreqs[0]) {
allEqual = false;
break;
}
}
if (allEqual) return true;
}
}
return false;
}
}
var equalFrequency = function(word) {
const freq = {};
for (let char of word) {
freq[char] = (freq[char] || 0) + 1;
}
const counts = Object.values(freq);
const countFreq = {};
for (let count of counts) {
countFreq[count] = (countFreq[count] || 0) + 1;
}
const uniqueCounts = Object.keys(countFreq).map(Number).sort((a, b) => a - b);
if (uniqueCounts.length === 1) {
const count = uniqueCounts[0];
return count === 1 || countFreq[count] === 1;
}
if (uniqueCounts.length === 2) {
const [low, high] = uniqueCounts;
const lowFreq = countFreq[low];
const highFreq = countFreq[high];
if (low === 1 && lowFreq === 1) return true;
if (high === low + 1 && highFreq === 1) return true;
if (low === 1 && high * highFreq === 1) return true;
return false;
}
return false;
};
复杂度分析
| 复杂度类型 | 值 |
|---|---|
| 时间复杂度 | O(n²) |
| 空间复杂度 | O(1) |
说明:
- 时间复杂度:外层循环 O(n),内层统计频次 O(n),检查频次相等 O(26) = O(1),总体 O(n²)
- 空间复杂度:使用固定大小的频次数组 O(26) = O(1)