我滴妈,今天这道力扣题真的让我觉得我是又聪明又笨!!!跟大家分享一下今天这个历程!真的是跌宕起伏!
今天早上起来之后听了昨天剩的JavaWeb的课,然后把毕业设计的一些材料整了整。整完差不多中午了。今天的阳光很不错,下午去公园玩了一圈。回来吃吃饭收拾收拾就快8点了。准备整一道力扣题就休息了。
题目是“给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的 字母异位词。”
md......上来就给我搞懵了。我看了看解释(字母异位词是通过重新排列不同单词或短语的字母而形成的单词或短语,并使用所有原字母一次)。这不就是判断字符串中的元素是不是都一样嘛.....真高端的词语啊!!!
看完解释后就觉得俩字----“秒了”
这不就是转化为字符数组,然后排个序,对比一下就好了。于是出现了方法一
public static boolean method1(String s, String t){
if (s.length() != t.length()){
return false;
}
char[] arr1 = s.toCharArray();
char[] arr2 = t.toCharArray();
Arrays.sort(arr1);
Arrays.sort(arr2);
return Arrays.equals(arr1,arr2);
}
上面的if判断纯纯是为了增加效率(也不知道能增加多少......ai建议我改一下)
好了,接下来就开始了方法大乱战以及我认为自己又聪明又笨!
我紧接着就想到了0424那篇中写的异或方法,我寻思着这部差不多,异曲同工。
int res = 0;
for (int i = 0; i < s.length(); i++) {
res ^= s.charAt(i);
res ^= t.charAt(i);
}
if (res == 0){
return true;
}else {
return false;
}
然后我就去测试了。我当时的测试数据是“s = abc”,“t = cba”
测试成功了。我瞬间觉得自己真聪明,真会举一反三。然后我就提交了。力扣又一次教我做人了。
人家给我一个测试用例是“s = a”,“t = ab”就不行了。因为根本到不了b,输出的是true。
后来我说ok,加个if判断,这总行了吧。结果力扣太牛了,人家给我来个aa和bb。。。又不行了
这个方法放弃了。
但是我又想到了0424那天的数组计数,这肯定可以。于是有了方法三
public static boolean method2(String s, String t){
if (s.length() != t.length()){
return false;
}
int[] arr = new int[26];
for (int i = 0; i < s.length(); i++) {
arr[s.charAt(i) - 'a']++;
arr[t.charAt(i) - 'a']--;
}
for (int i = 0; i < arr.length; i++) {
if (arr[i] != 0){
return false;
}
}
return true;
}
这个方法顺利通过了,但是运行时间5ms,只击败了20%的选手。那我看看第一名咋写的吧
方法四
public static boolean method4(String s, String t){
int[] arr = new int[26];
for (char c : s.toCharArray()) {
arr[c - 'a']++;
}
for (char c : t.toCharArray()) {
arr[c - 'a']--;
}
for (int i : arr) {
if (i != 0){
return false;
}
}
return true;
}
我当时还想着这不跟我的一样???他还没加判断,这能对???
结果人家还真是对的。关键点就在于循环的参数上。方法三是对着一个参数范围进行循环,如果不一样就会导致结果不对,因此需要判断。但是方法四是分别对自己的字符数组进行判断,就算长度不一样也不影响。
好了,那我就改一下方法三吧,我让他小于字符串长度最大的那个还想到了Math.max()方法(这里要注意,如果没有import static java.lang.Math.max;)就需要用类名调用方法。此时觉得自己厉害啊哈哈哈哈。但是!!!忘记了数组越界异常....哎.....
总结下来就是方法一,方法三,方法四的代码没有问题。
哦对了。
方法三和方法四都是使用 int[]
数组作为哈希表(Hash Table)来统计字符频率的,但它们的具体实现方式有所不同。
1. 都是“哈希”思想吗?
✅ 是的,它们都属于“哈希计数”方法,核心逻辑是:
- 使用一个 固定大小的数组(
int[26]
,对应 26 个小写字母)作为哈希表。 - 遍历
s
,统计每个字母的出现次数(++
)。 - 遍历
t
,减少每个字母的出现次数(--
)。 - 检查数组是否全为零:
- 如果全为零,说明
s
和t
是字母异位词(字符频率完全相同)。 - 如果有非零值,说明
s
和t
不是异位词。
- 如果全为零,说明
为什么是“哈希”?
- 数组的下标
c - 'a'
本质上是将字符'a'-'z'
映射(Hash) 到0-25
的索引,类似于哈希函数的键值计算。
2. 方法三 vs 方法四 的区别
虽然逻辑相同,但实现细节不同:
特性 | 方法三 | 方法四 |
---|---|---|
长度检查 | ✅ 有(提前优化) | ❌ 无 |
遍历方式 | 同时遍历 s 和 t | 分开遍历 s 和 t |
代码简洁性 | 稍复杂(双操作循环) | 更清晰(两次独立循环) |
越界风险 | 必须长度相同,否则会崩溃 | 无风险(独立遍历) |
适用场景 | 确保输入长度相同 | 通用,不关心长度 |
3. 严格来说,这是“哈希”还是“计数”?
- 计数数组(Counting Array):
这种固定大小的int[26]
是计数数组的经典实现,适用于小范围字符(如小写字母)。 - 哈希表(Hash Table):
如果字符范围更大(如 Unicode),通常会改用HashMap<Character, Integer>
实现真正的哈希表。
总的来说:
- 对于
a-z
的字母异位词问题,int[26]
是更高效的计数数组,但本质是哈希思想的简化版。 - 如果字符集更大(如包含大写、符号等),必须用
HashMap
实现通用的哈希表。