1.有效的字母异位词(242-易)
题目描述:给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词(字母相同位置不同)。进阶问题的核心点在于「字符是离散未知的」
示例:
输入: s = "anagram", t = "nagaram" 输出: true 输入: s = "rat", t = "car" 输出: false
思路:本题核心是记录出现字母的次数(数组或者hashmap),当两个字符串出现字母类别相同且数量相等,则为异位词。有两种思路:
- hashmap键值对进行记录是比较经典的方法。
- 也可以直接用数组记录字符出现的次数。
代码实现:
class Solution { // hashmap public boolean isAnagram1(String s, String t) { if (s.length() != t.length()) { return false; } HashMap<Character, Integer> map = new HashMap<>(); for (char cs : s.toCharArray()) { map.put(cs, map.getOrDefault(cs, 0) + 1); } for (char ct : t.toCharArray()) { map.put(ct, map.getOrDefault(ct, 0) - 1); if (map.get(ct) == -1) { return false; } } return true; } // 数组计数(推荐) public boolean isAnagram(String s, String t) { if (s.length() != t.length()) { return false; } // 记录字母出现的次数 int[] counts = new int[26]; for (char cs : s.toCharArray()) { counts[cs - 'a']++; } for (char ct : t.toCharArray()) { counts[ct - 'a']--; } for (int num : counts) { if (num != 0) { return false; } } return true; } }
ps:这个比较耗时,hashmap初始容量16,感觉扩容耗时!!
拓展:字母的异位词分组(T49)
思路:如何知道两个单词是异位词?排序或者计数!
- 排序:将排序后的字符串作为键(先转成字符数组,根据ascii码进行排序)
- 计数:自定义编码作为键
注意:map的key必须是String类型!不能是字符数组,原因是String重写了HashCode()和equalls()map.values()获取map的所有值。
代码实现:
class Solution { // 排序法(对每个字母进行重排序) public List<List<String>> groupAnagrams(String[] strs) { Map<String, ArrayList<String>> map = new HashMap<>(); for (String str : strs) { char[] chs = str.toCharArray(); Arrays.sort(chs); // 特别注意:要将字符数组转化为字符串,因为String实现了equals和hashCode方法 String key = String.valueOf(chs); if (!map.containsKey(key)) { map.put(key, new ArrayList<>()); } map.get(key).add(str); } return new ArrayList(map.values()); } // 计数法(两个数互为异位词:字母出现的次数相同) public List<List<String>> groupAnagrams(String[] strs) { Map<String, ArrayList<String>> map = new HashMap<>(); for (String str : strs) { char[] chs = str.toCharArray(); int[] counts = new int[26]; for (char c : chs) { counts[c - 'a']++; } // 将字符数组编码为字符串,比如将 [b,a,a,a,b,c] 编码成 a3b2c1 StringBuilder sb = new StringBuilder(); for (int i = 0; i < 26; i++) { if (counts[i] != 0) { sb.append((char)('a' + i)); sb.append(counts[i]); } } String key = sb.toString(); if (!map.containsKey(key)) { map.put(key, new ArrayList<>()); } map.get(key).add(str); } return new ArrayList(map.values()); } }
6.同构字符串(205-易)
题目描述:如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。
每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序!。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。
示例:
输入:s = "egg", t = "add" 输出:true 输入:s = "foo", t = "bar" 输出:false
思路:本题映射的关键是每个字符只能映射到一个字符(单映射,即s通过映射可以得到t),且映射位置不能错。解决方案:
- 常规解法:建立两个hashmap进行映射,如示例中,t 中的字符 a 和 r 虽然有唯一的映射 o,但对于 s 中的字符 o来说其存在两个映射 {a,r},故不满足条件。但这种方法效率比较低。
- 记录一个字符串上次出现的位置,如果两个字符串中字符上次出现的位置不一致,一定不是同构;否则,更新该字符上次出现的位置。
代码:
public boolean isIsomorphic(String s, String t) { Map<Character, Character> s2t = new HashMap<Character, Character>(); Map<Character, Character> t2s = new HashMap<Character, Character>(); int len = s.length(); for (int i = 0; i < len; ++i) { char x = s.charAt(i), y = t.charAt(i); if ((s2t.containsKey(x) && s2t.get(x) != y) || (t2s.containsKey(y) && t2s.get(y) != x)) { return false; } s2t.put(x, y); t2s.put(y, x); } return true; } public boolean isIsomorphic(String s, String t) { // 记录两个字符串每个字符上一次出现的位置(一共256个字符) int[] preIndexOfS = new int[256]; int[] preIndexOfT = new int[256]; for (int i = 0; i < s.length(); i++) { char sc = s.charAt(i), tc = t.charAt(i); if (preIndexOfS[sc] != preIndexOfT[tc]) { return false; } // 字符相同,更新位置 preIndexOfS[sc] = i + 1; preIndexOfT[tc] = i + 1; } return true; }
7.单词规律(290-易)
题目描述:给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。
示例:
输入: pattern = "abba", str = "dog cat cat dog" 输出: true
思路:基本思路与上题相同,创建两个hash表,建立双向映射(字符串和字符之间的映射)。注意:字符串的比较用equals,String对equals进行了重写,实际是比较的内容!
代码:
public boolean wordPattern(String pattern, String s) { HashMap<Character, String> map1 = new HashMap<>(); HashMap<String, Character> map2 = new HashMap<>(); String[] ss = s.split(" "); if (pattern.length() != ss.length) { return false; } int i = 0; for (char ch : pattern.toCharArray()) { if (map1.containsKey(ch) && !map1.get(ch).equals(ss[i]) || map2.containsKey(ss[i]) && !map2.get(ss[i]).equals(ch)) { return false; } map1.put(ch, ss[i]); map2.put(ss[i], ch); i++; } return true; }