我正在研究“分组字谜”。 问题陈述:给定字符串数组,将字谜分组在一起。
我可以对七字组进行分组,但是我无法避免已经分组的七字组。我想避免重复。一个元素只能属于一个组。在我的代码中,一个元素属于多个组。
这是我的代码:
public class GroupAnagrams1 {
public static void main(String[] args) {
String[] input = {"eat", "tea", "tan", "ate", "nat", "bat"};
List<List<String>> result = groupAnagrams(input);
for(List<String> s: result) {
System.out.println(" group: ");
for(String x:s) {
System.out.println(x);
}
}
}
public static List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> result = new ArrayList<List<String>>();
for(int i =0; i < strs.length; i++) {
Set<String> group = new HashSet<String>();
for(int j= i+1; j < strs.length; j++) {
if(areAnagrams(strs[i], strs[j])) {
group.add(strs[i]);
group.add(strs[j]);
}
}
if(group.size() > 0) {
List<String> aList = new ArrayList<String>(group);
result.add(aList);
}
}
return result;
}
这是检查两个字符串是否为字谜的方法。
private static boolean areAnagrams(String str1, String str2) {
char[] a = str1.toCharArray();
char[] b = str2.toCharArray();
int[] count1 = new int[256];
Arrays.fill(count1, 0);
int[] count2 = new int[256];
Arrays.fill(count2, 0);
for(int i = 0; i < a.length && i < b.length; i++) {
count1[a[i]]++;
count2[b[i]]++;
}
if(str1.length() != str2.length())
return false;
for(int k=0; k < 256; k++) {
if(count1[k] != count2[k])
return false;
}
return true;
}
}
预期输出:
group:
tea
ate
eat
group:
bat
group:
tan
nat
实际输出:
group:
tea
ate
eat
group:
tea
ate
group:
tan
nat
组的显示顺序无关紧要。显示方式无关紧要。
首选项:请随时使用HashMaps提交解决方案,但我更喜欢在不使用HashMaps和Java8的情况下查看解决方案
答案 0 :(得分:1)
我使用流时会采取略有不同的方法:
public class Scratch {
public static void main(String[] args) {
String[] input = { "eat", "tea", "tan", "ate", "nat", "bat" };
List<List<String>> result = groupAnagrams(input);
System.out.println(result);
}
private static List<List<String>> groupAnagrams(String[] input) {
return Arrays.asList(input)
// create a list that wraps the array
.stream()
// stream that list
.map(Scratch::sortedToOriginalEntryFor)
// map each string we encounter to an entry containing
// its sorted characters to the original string
.collect(Collectors.groupingBy(Entry::getKey, Collectors.mapping(Entry::getValue, Collectors.toList())))
// create a map whose key is the sorted characters and whose
// value is a list of original strings that share the sorted
// characters: Map<String, List<String>>
.values()
// get all the values (the lists of grouped strings)
.stream()
// stream them
.collect(Collectors.toList());
// convert to a List<List<String>> per your req
}
// create an Entry whose key is a string of the sorted characters of original
// and whose value is original
private static Entry<String, String> sortedToOriginalEntryFor(String original) {
char c[] = original.toCharArray();
Arrays.sort(c);
String sorted = new String(c);
return new SimpleEntry<>(sorted, original);
}
}
这将产生:
[[eat, tea, ate], [bat], [tan, nat]]
如果您想消除重复的字符串(例如,如果“ bat”在您的输入中出现两次),则可以在toSet()
调用中调用toList()
而不是Collectors.groupingBy
,并更改返回类型。
答案 1 :(得分:1)
我也建议为此使用java流。因为您不希望这是另一种解决方案:
public static List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> result = new ArrayList<>();
for (String str : strs) {
boolean added = false;
for (List<String> r : result) {
if (areAnagrams(str, r.get(0))) {
r.add(str);
added = true;
break;
}
}
if (!added) {
List<String> aList = new ArrayList<>();
aList.add(str);
result.add(aList);
}
}
return result;
}
解决方案中的问题是,您将每个迭代向前推进了一个步骤,因此您只生成了不完整的组["tea", "ate"]
而不是["bat"]
。
我的解决方案使用另一种方法来检查您是否有一个组,其中第一个单词是所搜索单词的字谜。如果没有,请创建一个新组并继续。
因为我将使用Java Streams,就像我在一开始所说的那样,这是我使用流的最初解决方案:
List<List<String>> result = new ArrayList<>(Arrays.stream(words)
.collect(Collectors.groupingBy(w -> Stream.of(w.split("")).sorted().collect(Collectors.joining()))).values());
要生成排序后的字符串键以对字母进行分组,可以查看here获得更多解决方案。
结果是我提供的两个解决方案都是这样:
[[eat, tea, ate], [bat], [tan, nat]]