给定两个字符串st ,编写一个函数来判断t是否是s的字母异位词。

示例1:

输入: s = "anagram", t = "nagaram"
输出: true

示例2:

输入: s = "rat", t = "car"
输出: false

说明: 你可以假设字符串只包含小写字母。

进阶: 如果输入字符串包含Unicode字符怎么办?你能否调整你的解法来应对这种情况?

来源:力扣(LeetCode
链接:https://leetcode-cn.com/problems/valid-anagram
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:首先考虑特殊情况,如果字符串st都为空字符串,则可认为它们互为字母异位词;如果字符串st连长度都不相等,则一定不为字母异位词。然后再考虑如何进行判断,两个字符串中都只包含26个小写的英文字母,如果两个字符串是字母异位词,那么一定满足:每一个小写英文字母在字符串s中出现的次数等于在字符串t中出现的次数。于是,我们只需分别对每一个小写英文字母在两个字符串中出现的次数进行计数,然后比较是否相等即可。

具体到代码实现层面,由于两个字符串互为字母异位词的前提条件是其长度相等,所以在计数时我们可以同时对两个字符串进行遍历;另一方面,分别计数可以转换成对第一个字符串s进行计数累加,而对另一个字符串t进行计数递减,最终如果每一个小写英文字母的累计结果为0,则可明确这两个字符串互为字母异位词,只要出现一个小写英文字母的累计结果不为0,则不满足条件。

如何对每一个小写英文字母进行计数?可定义一个长度为26int数组,字母az依次对应下标索引025。在遍历字符串s时,每次遇到a则将索引0位置的值加一,以此类推;而当遍历字符串t时,每次遇到a则将索引0位置的值减一。直到遍历完成,判断数组中的所有元素是否都为0,如果是,则st互为字母异位词,否则不是。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public boolean isAnagram(String s, String t) {
// 1. 空字符串情况
if (s.length() == 0 && t.length() == 0) {
return true;
}
// 2. 长度不相等情况
if (s.length() != t.length()) {
return false;
}
// 字符串转字符数组
char[] sc = s.toCharArray();
char[] tc = t.toCharArray();
// 定义字母计数器
int[] counter = new int[26];
// 同时遍历两个字符数组
for (int i = 0,length = sc.length;i < length;i++) {
// 字母对应下标位置的值加一
counter[sc[i] - 'a']++;
// 字母对应下标位置的值减一
counter[tc[i] - 'a']--;
}
// 遍历计数器
for (int c : counter) {
// 存在一个计数不为0的则可确定不是字母异位词
if (c != 0) return false;
}
return true;
}

进阶问题:

如果输入字符串包含Unicode字符怎么办?

思路:Unicode字符可定义100万个以上的唯一字符,如果还选择使用数组来计数,那么需要开辟出一块非常大的内存空间,而且最后遍历数组也是非常耗时的。于是我们可以考虑使用哈希表来计数,可以适应任意的字符范围,另外最后遍历哈希表判断计数结果时,可将哈希表中的数据去重,即转换成一个无序不重复的Set集合,利用空间换时间,如果该集合中只存在一个元素且值为0,则字符串st互为字母异位词,否则不是。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public boolean isAnagram(String s, String t) {
// 1. 空字符串情况
if (s.length() == 0 && t.length() == 0) {
return true;
}
// 2. 字符串长度不相等
if (s.length() != t.length()) {
return false;
}
// 字符串转字符数组
char[] sc = s.toCharArray();
char[] tc = t.toCharArray();
// 哈希表计数器
Map<Character,Integer> counter = new HashMap<>();
// 遍历第一个字符串s
for (int i = 0,sLength = s.length();i < sLength;i++) {
Integer val = counter.get(sc[i]);
if (val != null) {
// 如果当前字符已存在于哈希表中,则计数加一
val += 1;
counter.put(sc[i],val);
} else {
// 当前字符第一次出现,则计数为1
counter.put(sc[i],1);
}
}
// 遍历第二个字符串t
for (int j = 0,tLength = t.length();j < tLength;j++) {
Integer val = counter.get(tc[j]);
if (val != null) {
// 如果当前字符已存在于哈希表中,则计数减一
val -= 1;
counter.put(tc[j],val);
} else {
// 当前字符第一次出现,已经可以断定不是字母异位词
return false;
}
}
// 方案一:遍历哈希表计数器
/**
Iterator<Map.Entry<Character,Integer>> iterator = counter.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Character,Integer> entry = iterator.next();
// 如果计数结果不等于0,则不是字母异位词
if (entry.getValue() != 0) {
return false;
}
}
**/
// 方案二:将哈希表转成无序不重复的Set集合
Set<Integer> set = new HashSet<>(counter.values());
// 如果Set集合中的元素个数不为1,或者唯一一个元素的值不为0,则可确定不是字母异位词。
if (set.size() != 1 || !set.contains(0)) {
return false;
}
return true;
}