242. 有效的字母异位词
思路:
利用哈希表,统计两字符串的字符出现次数,最后比较对应字符出现次数是否相等即可。
法一:数组作为哈希表,利用字符对应的ASCII。
法二:直接使用HashMap
法三:快排,异位词其实就是两个相同字符串被打乱字符顺序而产生的。
public boolean isAnagram(String s, String t) {
//如果仅仅是26字母--可不用HashMap--原始哈希表(数组)
int ls = s.length();
if(ls != t.length()) return false;
int[] num = new int[26];
for(int i = 0;i < ls; i++) {
// 不需要分开统计,直接中和即可。
num[s.charAt(i)-'a']++;
num[t.charAt(i)-'a']--;
}
for(int n : num){
if(n != 0) return false;
}
return true;
}
输入字符串包含 unicode 字符
public boolean isAnagram(String s, String t) {
int l = s.length();
if(l != t.length()) return false;
HashMap<Character,Integer> map = new HashMap<>();
for(int i =0 ;i < l; i++) {
//getOrDefault 不存在则置0
map.put(s.charAt(i),map.getOrDefault(s.charAt(i),0) + 1);
map.put(t.charAt(i),map.getOrDefault(t.charAt(i),0) - 1);
}
for(Integer value : map.values()) {
if(value != 0) return false;
}
return true;
}
//快排
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()) return false;
char[] cs = s.toCharArray();
char[] ct = t.toCharArray();
Arrays.sort(cs);
Arrays.sort(ct);
return Arrays.equals(cs,ct);
}
49. 字母异位词分组
思路:
首先是对异位词的判断(具体见题有效字母异位词),然后需要对异位词进行分组,是异位词的都归为一组。
法一:哈希表判断两字符串是否是异位词,两重for循环进行分组
法二:快排判断异位词+哈希表存放结果
法三:哈希表判断异位词+哈希表存放结果
法一:不推荐,没把哈希表用到极致
public List<List<String>> groupAnagrams(String[] strs) {
//其中对于遍历过的无需再次遍历--hash数组的作用
List<List<String>> res = new ArrayList<>();//结果
int l = strs.length;
boolean[] hash = new boolean[l];//记录遍历过的
for (int i = 0; i < l; i++) {
if (hash[i] == false) {
hash[i] = true;
List<String> tmp = new ArrayList<>();
tmp.add(strs[i]);
for (int j = i + 1; j < l; j++) {
if (hash[j] == false) {
//异位词判断
if (hashJudge(strs[i], strs[j])) {
hash[j] = true;
tmp.add(strs[j]);
}
}
}
res.add(tmp);
}
}
return res;
}
boolean hashJudge(String s1, String s2) {
int l = s1.length();
if(l != s2.length()) return false;
int[] arr = new int[26];
for(int i =0 ; i < l; i++) {
arr[s1.charAt(i) - 'a']++;
arr[s2.charAt(i) - 'a']--;
}
for(int i =0 ; i < 26; i++) {
if(arr[i] != 0) return false;
}
return true;
}
法二:
public List<List<String>> groupAnagrams(String[] strs) {
int l = strs.length;
Map<String, List<String>> map = new HashMap<>();
for(int i = 0; i < l; i++) {
//快排
char[] tmp = strs[i].toCharArray();
Arrays.sort(tmp);
//排序后的字符串作为key
String key = new String(tmp);
//异位词集合作为value
List<String> list = map.getOrDefault(key, new ArrayList<String>());
list.add(strs[i]);
map.put(key,list);
}
return new ArrayList<List<String>>(map.values());
}
public List<List<String>> groupAnagrams(String[] strs) {
int l = strs.length;
Map<String, List<String>> map = new HashMap<>();
for(int i = 0; i < l; i++) {
//快排
int[] count = new int[26];
int len = strs[i].length();
for(int j = 0; j < len; j++) {
count[strs[i].charAt(j) - 'a']++;
}
StringBuilder sb = new StringBuilder();
//由于遍历从0开始,拼接的串也是有序的,即可作为key
//拼接时需要拼上次数,代替重复拼接,例如 aaaab --> a4b(唯一)
for(int j = 0; j < 26; j++){
if(count[j] != 0) sb.append('a'+ j).append(count[j]);//(char)('a'+ j)转不转都行
}
String key = sb.toString();
//异位词集合作为value
List<String> list = map.getOrDefault(key, new ArrayList<String>());
list.add(strs[i]);
map.put(key,list);
}
return new ArrayList<List<String>>(map.values());
}
409. 最长回文串
思路:
哈希表,统计串中字符的出现次数,其中次数为2的倍数即可加入回文串,这里不是指奇偶数之分,因为5虽然为奇数,但包含4可用,即次数为偶数的话,直接加入回文串,为奇数则减一。最后需要注意一点的,回文串中可以出现有且仅有一个单个字符。所以如果统计过程中出现奇数个数的字符,需要做标记。
public int longestPalindrome(String s) {
//哈希数组
int[] hash= new int[58]; // 'Z' - 'a'
int l = s.length();
for(int i = 0; i < l; i++) {
hash[s.charAt(i)-'A']++;
}
int res = 0;
// 判断是否存在奇数个数的字母--只要存在,最后的长度需加一
boolean tf = false;
for(int i = 0; i < 58; i++) {
if((hash[i] & 1) == 0) {
//偶数
res = res + hash[i];
} else {
//奇数减一
res = res + hash[i] - 1;
tf = true;
}
}
return tf == true ? res+1 : res;
}
除了用标记,也可以用
res < l ? res + 1 : res
也就是如果结果等于l,说明不存在奇数个数的字母。全是偶数的,反之亦然。
*205. 同构字符串
思路:
由于题目规定字符顺序不可变,说明串与串之间的字符都是一一对应的,每一个映射关系可以出现多次,但必须相同,例如e->a,后面e->b或者b->a就不行了。
根据映射关系唯一,可通过下标来标识,如果两字符构成的映射关系所对应的下标相同则符合。
public boolean isIsomorphic(String s, String t) {
HashMap<Character,Integer> h1 = new HashMap<>();
HashMap<Character,Integer> h2 = new HashMap<>();
for(int i = 0; i < s.length(); i++) {
Integer a = h1.get(s.charAt(i));
Integer b = h2.get(t.charAt(i));
if(a == null && b == null) { //不存在该映射则添加
h1.put(s.charAt(i),i);
h2.put(t.charAt(i),i);
} else if(a != null && b != null) {// 存在则判断下标是否相同
if(!a.equals(b)) return false;
} else {
return false; //仅存在一个也不符合,即相同字符映射到了不同字符上
}
}
return true;
}
简化
public boolean isIsomorphic(String s, String t) {
HashMap<Character,Integer> h1 = new HashMap<>();
HashMap<Character,Integer> h2 = new HashMap<>();
for(int i = 0; i < s.length(); i++) {
Integer a = h1.get(s.charAt(i));
Integer b = h2.get(t.charAt(i));
if(a == null && b == null) { //不存在该映射则添加
h1.put(s.charAt(i),i);
h2.put(t.charAt(i),i);
}
if(a != b) return false;
}
return true;
}
如果你是最后一个测试用例没通过,那可能是常量池问题:链接,就是你比较的时候用==或!=,而没用equals,例如129个a的字符串,如果一直put的话,最后一个的value是new Integer(128),也就是对象,而不是常量。你可能会发现上面第二个题解没问题,而链接里的存在问题,这是因为题解添加元素时是有判断的if(a == null && b == null)
,它是只有不存在才添加,也就是对于129个a的字符串,他只会添加第一个。其实该题针对的是可打印的acsii,也就是(32-127),即便不相同的字符也不会出现常量池问题,否则第二个题解同样错。
其它测试用例
"badc"
"baba"
"bbbaaaba"
"aaabbbba"