更好的解决“之前和之后”难题的方法?

时间:2019-05-03 21:20:51

标签: java

这是我接受Hack​​errank编码挑战的新Grad软件工程师职位的采访:

拼图前后

给出一个短语列表,生成一个命运之轮“之前和之后”难题的列表。 “之前和之后”难题是一个短语以另一个单词的最后一个单词结尾的地方。 作为一个基本示例,给定两个短语“编写代码”和“代码摇滚”,输出将为“编写代码摇滚”。 这是输入和预期输出的更全面示例。

输入= [      “任务说明”,      “快点吃”,      “一个模子出来的”,      “巧克力吧”,      “不可能完成的任务”,      “一个执行任务的人”,      “集体聚会”,      “吃我的话”,      “肥皂” ]

输出= [      “咬一口吃我的话”,      “从旧的聚会中脱颖而出”,      “巧克力肥皂”,      “一个正在执行任务的人”,      “一个不可能完成任务的人” ]

约束: -返回的结果不必唯一 -不用担心肠衣。假设输入仅是带空格的-z小写字母(因此没有特殊字符或处理大小写匹配的单词) -假设所有空格都是单个空格(无需担心空白边缘情况)

我通过完成6/7个测试用例解决了这个问题,其中最后一个测试用例由于超时而终止。

// 1. Submitted code during the interview (cleared 6/7 test-cases) 

/*
My approach: Compare every phrase in the list to every other phrase in the list.
If the last word of the phrase matches the first word of another phrase, do the
following: 
1) Remove the first word from the other phrase.
2) Combine both phrases
3) Add the combined phrase to the output list.
*/

private static List<String> generate_phrases(List<String> phrases) {
        List<String> output = new ArrayList<>();
        for(String temp: phrases) {
            String[] content = temp.split(" ");
            for(String temp2: phrases) {
                String[] content2 = temp2.split(" ");
                if(content[content.length-1].equals(content2[0])) {
                    String tempWord = content2[0];
                    temp2 = temp2.replaceAll(tempWord, "");
                    output.add(temp+temp2);
                }
            }
        }
        return output;
    }

// 2. Improved code after the interview

/* 
My approach: Compare every phrase in the list to every other phrase in the list.
If the last word of the phrase matches the first word of another phrase, do the
following: 
1) Push both phrases to a set.
2) Remove the first word from the other phrase.
3) Combine both phrases
4) Add the combined phrase to the output list.
5) for all phrases added in the set, decrease the size of the input list by removing the visited phrases
*/

private static List<String> generate_phrases(List<String> phrases) {
        List<String> output = new ArrayList<>(); boolean flag = false;
        int length = phrases.size();
        for(int i=0; i<length; i++) {
            Set<String> wordsToRemove = new HashSet<>();
            String temp = phrases.get(i);
            String[] contents1 = temp.split(" ");
            for(int j=0; j<length; j++) {
                String temp2 = phrases.get(j);
                String[] contents2 = temp2.split(" ");
                if(contents1[contents1.length-1].equals(contents2[0])) {
                    flag = true;
                    wordsToRemove.add(temp); wordsToRemove.add(temp2);
                    String tempWord = contents2[0];
                    temp2 = temp2.replaceAll(tempWord, "");
                    output.add(temp+temp2);
                }
            }
            if(flag) {
                for (String s : wordsToRemove){ phrases.remove(s); length--; }
                flag = false; i=0;
            }
        }
        return output;
    }

我提交了我的第一段代码,该段代码未能通过最后一个测试用例,并因超时而终止。这是我在面试中能做的最好的事情。后来在面试之后花了一些时间,我想出了一个有效的解决方案,与以前的迭代相比,迭代次数更少。但是我所有的解决方案都使用2 for循环。有人可以帮助我提供更好,更高效的代码吗?

按照帖子中评论的方法,我想出了一个解决方案。但是仍然不知道它是否会更有效。现在我的代码的时间复杂度是多少?

public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("mission statement");
        list.add("a quick bite to eat");
        list.add("a chip off the old block");
        list.add("chocolate bar");
        list.add("mission impossible");
        list.add("a man on a mission");
        list.add("block party");
        list.add("eat my words");
        list.add("bar of soap");
        System.out.println(generate_phrases(list));
    }

private static List<String> generate_phrases(List<String> phrases) {
        List<Phrase> phraseList = generatePhraseList(phrases);
        Map<String, List<Phrase>> map = generateHashMapOfUniqueKeys(phraseList);
        return generateOutputList(map);
    }

    private static List<Phrase> generatePhraseList(List<String> phrases) {
        List<Phrase> phraseList = new ArrayList<>();
        for (String p: phrases) {
            Phrase temp = new Phrase(p);
            phraseList.add(temp);
        }
        return phraseList;
    }

    private static Map<String, List<Phrase>> generateHashMapOfUniqueKeys(List<Phrase> phraseList) {
        Map<String, List<Phrase>> map = new HashMap<>();
        for(Phrase p : phraseList) {
            String start = p.getStart();
            if(!map.containsKey(start)) {
                List<Phrase> temp = new ArrayList<>();
                temp.add(p);
                map.put(start, temp);
            } else {
                map.get(start).add(p);
            }
        }
        return map;
    }

    private static List<String> generateOutputList(Map<String, List<Phrase>> map) {
        List<String> output = new ArrayList<>();
        for(List<Phrase> list: map.values()) {
            for(Phrase p: list) {
                String keyToBeSearched = p.getEnd();
                if(map.containsKey(keyToBeSearched)) {
                    List<Phrase> temp = map.get(keyToBeSearched);
                    for(Phrase p2: temp) {
                        output.add(p.getWhole()+" "+p2.getMiddle());
                    }
                }
            }
        }
        return output;
    }

}

class Phrase {
    private final String start;
    private final String middle;
    private final String end;
    private final String whole;

    public Phrase(String initial) {
        this.whole = initial;
        String[] words = initial.split(" ");
        this.start = words[0];
        this.middle = Arrays.stream(words, 1, words.length).collect(joining(" "));
        this.end = words[words.length - 1];
    }

    public String getStart() {
        return this.start;
    }

    public String getMiddle() {
        return this.middle;
    }

    public String getEnd() {
        return this.end;
    }

    public String getWhole() {
        return this.whole;
    }

}

3 个答案:

答案 0 :(得分:0)

可能值得封装收集拆分操作的结果,这样就不必重复多次:

class Phrase {
    private final String start;
    private final String middle;
    private final String end;

    public Phrase(String initial) {
        String[] words = initial.split(" ");
        start = words[0];
        middle = Arrays.stream(words, 1, words.length).collect(joining(" "));
        end = words[words.length - 1];
    }

    public boolean matches(Phrase other) {
        this.end.equals(other.start);
    }

    public String combine(Phrase other) {
        assert matches(other);
        return Stream.of(this.start, this.middle, this.end, other.middle, other.end)
            .collect(joining(" "));
    }
}

然后您的生成代码就变得非常简单:

List<Phrase> phrases = Arrays.stream(inputPhrases).map(Phrase::new).collect(toList());
return phrases.stream()
    .flatMap(ph1 -> phrases.stream().filter(ph1::matches).map(ph1::combine))
    .collect(toList());

我不确定这会更有效,但是我的猜测是,对于较小的输入,效率会较低,而对于较大的输入,效率会更高。如果您的硬件可以利用流,您还可以使流并行利用多个执行线程。

答案 1 :(得分:0)

要解决此问题(以及许多其他问题)的良好时间复杂性,需要缩减搜索空间。这是一种方法:

有些结束词/开始词对是唯一的。首先将它们配对。使用HashMap将短语分组在其起始词和结束词下。分组后,将大小为1的组配对,然后将其从地图上删除。

继续使用地图,对其余所有对使用depth first search(具有向后跟踪)。

HashMap将为您提供恒定的时间搜索速度。

答案 2 :(得分:0)

使用HashMap将起始世界存储为键和关联的字符串列表。因此,当我们进行搜索时,它变为O(N)遍历 Total Time = O(2N)前N个将数据存储在HashMap中。第二个N做查询