原题
Given an arbitrary ransom note string and another string containing letters from all the magazines, write a function that will return true if the ransom note can be constructed from the magazines ; otherwise, it will return false.
Each letter in the magazine string can only be used once in your ransom note.
Note:
You may assume that both strings contain only lowercase letters.
canConstruct("a", "b") -> false
canConstruct("aa", "ab") -> false
canConstruct("aa", "aab") -> true
题目要求
题目叫做Ransom Note,勒索信,刚开始我还没理解这个题目的意思,尤其这个标题,和magazine有啥关系呢?后来仔细想想,才慢慢理解。勒索信,为了不暴露字迹,就从杂志上搜索各个需要的字母,组成单词来表达的意思。这样来说,题目也就清晰了,判断杂志上的字是否能够组成勒索信需要的那些字符。
这里需要注意的就是杂志上的字符只能被使用一次,不过不用考虑大小写的问题。
有一种最简单的理解就是对于ransomNote里每个字符出现的次数必须小于或者等于该字符在magazine出现的次数。
解法
解法一:这种解法比较简单,就是将ransomNote的字符串挨个遍历,每个字符再从magazine里遍历匹配,只是再创建了个byte数组,数组每个元素的索引表示magazine字符串的位置,元素值表示是否被校验过,0表示还未被校验过,非0就表示该位置已经被校验过。不过这种做法效率不高。
public boolean canConstruct(String ransomNote, String magazine) {
boolean ret = true;
byte[] bytes = new byte[magazine.length()];
for (int i = 0; i < ransomNote.length(); i++) {
char c = ransomNote.charAt(i);
boolean found = false;
for (int j = 0; j < magazine.length(); j++) {
if (bytes[j] == 0 && magazine.charAt(j) == c) {
bytes[j]++;
found = true;
break;
}
}
if (!found) {
ret = false;
break;
}
}
return ret;
}
解法二:将ransomNote和magazine都从小到大排序,然后对ransomNote遍历,同时在magazine中匹配,如果匹配到了,则记住此时magazine中字符的索引,便于下次操作,因为都是排序的。如果直到magazine中字符已经大于ransomNote中字符了,就说明就再也匹配不到了,则表示匹配失败。
public boolean canConstruct(String ransomNote, String magazine) {
boolean ret = true;
char[] ra = ransomNote.toCharArray();
Arrays.sort(ra);
char[] ma = magazine.toCharArray();
Arrays.sort(ma);
int index = 0;
boolean found = true;
for (int i = 0; i < ra.length && ret; i++) {
char ri = ra[i];
found = false;
for (int j = index; j < ma.length; j++) {
if (ma[j] > ri) {
ret = false;
break;
} else if (ma[j] == ri) {
index++;
found = true;
break;
} else {
index++;
}
}
if (!found) {
ret = false;
break;
}
}
return ret;
}
解法三:这是LeetCode Discuss中的最热代码,它的原理就是列出了magazine的字母表,然后算出了出现个数,然后遍历ransomNote,保证有足够的字母可用,代码非常清晰。
public boolean canConstruct(String ransomNote, String magazine) {
int[] arr = new int[26];
for (int i = 0; i < magazine.length(); i++) {
arr[magazine.charAt(i) - 'a']++;
}
for (int i = 0; i < ransomNote.length(); i++) {
if(--arr[ransomNote.charAt(i)-'a'] < 0) {
return false;
}
}
return true;
}