如何使用Java在复合/简单单词中找到简单的单词?

时间:2016-11-12 00:00:27

标签: java algorithm trie

我有一个单词列表,其中包含“简单”和“复合”单词,并且希望实现一个算法,该算法打印出一个单词列表,而不包含由简单单词组成的复合单词。

Sampel输入:

chat, ever, snapchat, snap, salesperson, per, person, sales, son, whatsoever, what, so

期望的输出:

chat, ever, snap, per, sales, son, what, so

我已经写了以下内容,但是如何从这里开始接受它:

private static String[] find(String[] words) {

    ArrayList<String> alist = new ArrayList<String>();
    Set<String> r1 = new HashSet<String>();
    for(String s: words){
        alist.add(s);
    }
    Collections.sort(alist,new Comparator<String>() {

        public int compare(String o1, String o2) {

            return o1.length()-o2.length();
        }
    });

    int count= 0;
    for(int i=0;i<alist.size();i++){
        String check = alist.get(i);
        r1.add(check);
        for(int j=i+1;j<alist.size();j++){

            String temp = alist.get(j);
            //System.out.println(check+" "+temp);
            if(temp.contains(check) ){

                alist.remove(temp);

            }
        }
    }
    System.out.println(r1.toString());
    String res[] = new String[r1.size()];
    for(String i:words){
        if(r1.contains(i)){
            res[count++] = i;
        }
    }

    return res;
}

任何有关更好方法的指导/见解或建议都将受到赞赏。

3 个答案:

答案 0 :(得分:2)

我试图通过你的代码,看起来像“儿子”不在你的输出中。我相信它因为这条线而失败了:

if(temp.contains(check)) { <-- wrong check.
    alist.remove(temp); 
}

因此,不应只是检查temp.contains(check),而应该有一个小循环来执行以下操作:

  1. check开始临时?
  2. 如果1)通过,那么让temp = temp.substring(check.length),然后再回到1),直到temp ==“”;
  3. 另一个实现是设置trie(https://en.wikipedia.org/wiki/Trie)并使用它进行检查?

    1. 根据字长
    2. 对单词列表进行排序
    3. 这个词的前言,如果单词不在trie中,请将其添加到trie中。否则,这可能是复制词或复合词
    4. 使用DFS将trie输出到单词列表中。
    5. 步骤1确保当你检查一个复合词时,它的简单词已经在trie中。

答案 1 :(得分:1)

这是我直截了当的n ^ 2解决方案:

static String[] simpleWords(String[] words) {
    String[] result;
    HashSet<Integer> map = new HashSet<>();
    for(int i = 0; i < words.length; i++) {
        String word = words[i];
        for(int j = 0; j < words.length; j++) {
            if(j != i) {
                word = word.replaceAll(words[j], "");
            }
        }
        if(!word.equals("")) {
            map.add(i);
        }
    }
    result = new String[map.size()];
    int i = 0;
    for(int index: map) {
        result[i] = words[index];
        i++;
    }
    return result;
}

答案 2 :(得分:0)

我没有尝试在您的代码中找到错误,而是使用简单的循环和递归辅助方法编写自己的impl:

private static String[] find(String[] array) {
    Set<String> words = new LinkedHashSet<>(Arrays.asList(array));
    Set<String> otherWords = new HashSet<>(words);
    for (Iterator<String> i = words.iterator(); i.hasNext(); ) {
        String next = i.next();
        otherWords.remove(next);
        if (isCompound(next, otherWords)) {
            i.remove();
        } else {
            otherWords.add(next);
        }
    }
    return words.stream().toArray(String[]::new);
}

private static boolean isCompound(String string, Set<String> otherWords) {
    if (otherWords.contains(string)) {
        return true;
    }
    for (String word : otherWords) {
        if (string.startsWith(word)) {
            return isCompound(string.replaceAll("^" + word, ""), otherWords);
        }
        if (string.endsWith(word)) {
            return isCompound(string.replaceAll(word + "$", ""), otherWords);
        }
    }
    return false;
}

请参阅live demo

这会产生您想要的输出,这需要保留字序。

解释

复合词仅由列表中的其他词组成。重要的是,这意味着复合词以其他词开始结束。我们可以使用这个事实来检查开始/结束,而不是在单词中的每个位置搜索其他单词,这大大简化了代码。

因此:对于列表中的每个单词,如果它以另一个单词开头/结尾,则删除该单词并重复该过程,直到没有任何内容为止,此时您知道单词是复合词。

一组“其他单词”,即删除了当前单词的完整集,将传递给辅助方法以进一步简化代码。