繁体   English   中英

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

[英]Better solutions for “Before and After” puzzle?

这是我在接受新Grad软件工程师职位的采访时遇到的Hackerrank编码挑战:

拼图前后

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

输入= [“任务陈述”,“快速吃东西”,“从旧块上掉下来”,“巧克力棒”,“不可能完成任务”,“有任务的人”,“阻止聚会”,“吃我的话语“,”肥皂“

输出= [“快速咬一口吃我的话”,“从旧的聚会上摘下一块碎屑”,“巧克力肥皂”,“一个执行任务的人”,“一个无法执行任务的人”]

限制:-返回的结果不必唯一-不必担心大小写。 假设输入仅是带有空格的-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;
    }

}

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

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());

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

要解决此问题(以及许多其他问题),要获得良好的时间复杂度,就必须减少搜索空间。 这是一种方法:

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

继续使用地图,对所有其余对使用深度优先搜索 (带有向后跟踪)。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM