¿Mejores soluciones para el rompecabezas "Antes y después"?

Este fue mi desafío de codificación de Hackerrank para una entrevista para un nuevo puesto de ingeniero de software graduado:

Rompecabezas antes y después

Dada una lista de frases, genera una lista de acertijos "Antes y después" de Wheel of Fortune. Los rompecabezas "Antes y después" son donde una frase termina con la última palabra de la primera de otra. Como ejemplo básico, dadas dos frases, "escribir código" y "código de rocas", la salida será "escribir código de rocas". Aquí hay un ejemplo más completo de entradas y salidas esperadas.

input = ["declaración de misión", "un bocado rápido para comer", "un trozo del viejo bloque", "barra de chocolate", "misión imposible", "un hombre en una misión", "fiesta de barrio", "comer mis palabras "," pastilla de jabón "]

salida = ["un bocado rápido para comer mis palabras", "un chip de la vieja fiesta de la cuadra", "barra de jabón de chocolate", "un hombre en una declaración de misión", "un hombre en una misión imposible"]

Restricciones: - Los resultados devueltos no tienen que ser únicos - No se preocupe por la carcasa. Suponga que las entradas son solo a - z letras minúsculas con espacios (por lo que no hay caracteres especiales ni palabras que coincidan con mayúsculas y minúsculas).

Resolví el problema completando 6/7 casos de prueba donde el último caso de prueba terminó debido al tiempo de espera.

// 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;
    }

Envié mi primer código que falló en el último caso de prueba y me terminaron debido al tiempo de espera. Esto fue lo mejor que pude hacer durante la entrevista. Luego de pasar un tiempo después de la entrevista, se me ocurrió una solución eficiente que requirió menos iteraciones que la anterior. Pero todas mis soluciones usan 2 para bucles. ¿Alguien puede ayudarme con un código mejor y eficiente?

Siguiendo el enfoque de los comentarios en mi publicación, se me ocurrió una solución. Pero aún no puedo entender si será más eficiente. ¿Cuál será la complejidad temporal de mi código ahora?

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;
    }

}
Respuesta 1

Obtener una buena complejidad de tiempo para este problema (y muchos otros) requiere reducir el espacio de búsqueda. Aquí hay una manera:

Hay algunos pares de palabras final / inicial que son únicos. Combínalos primero. Use un HashMap para agrupar frases debajo de sus palabras iniciales y finales. Después de agrupar, empareje grupos de tamaño uno y retírelos del mapa.

Continuando con el mapa, utilice una búsqueda en profundidad primero (con seguimiento hacia atrás) en todos los pares restantes.

El HashMap le dará una velocidad de búsqueda de tiempo constante.

Respuesta: 2

En Europa Central, la zona horaria actual (a partir del 3 de octubre) es: CEST / UTC + 2 Pero cuando creo una instancia de SimpleDateFormat en Android ahora ... dateTimeFormat = new SimpleDateFormat ("aaaa-MM-dd '...

Estoy tratando de conectarme a una base de datos mysql, pero sigo recibiendo el error: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: error en el enlace de comunicaciones. ..

Quiero extraer toda la anotación de mi objeto JSONArray y encontré esta publicación https://www.mkyong.com/java/json-simple-example-read-and-write-json/. El problema es que mi JSONArray es una especie de "...

Posible duplicado: ¿Cómo puedo interactuar con el diseño de la pantalla de bloqueo para mostrar texto en él, como esta aplicación: Quiero mostrar algo de texto en la pantalla de bloqueo de Android igual que la aplicación de Android 'Me gusta' ...