Número de maneras de recrear una cadena dada usando una lista de palabras dadas

Inashidag:

Dada es una cadena wordy una matriz de cadenas bookque contiene algunas cadenas. El programa debe dar a conocer el número de posibilidades para crear wordsólo con elementos en book. Un elemento se puede utilizar tantas veces como queramos y el programa debe terminar en menos de 6 segundos .

Por ejemplo, la entrada:

String word = "stackoverflow";

String[] book = new String[9];
book[0] = "st";
book[1] = "ck";
book[2] = "CAG";
book[3] = "low";
book[4] = "TC";
book[5] = "rf";
book[6] = "ove";
book[7] = "a";
book[8] = "sta";

La salida debe ser 2, ya que podemos crear "stackoverflow"de dos formas:

1: "st"+ "a"+ "ck"+ "ove"+ "rf"+"low"

2: "sta"+ "ck"+ "ove"+ "rf"+"low"

Mi aplicación del programa sólo termina en el tiempo requerido, si wordes relativamente pequeñas (<15 caracteres). Sin embargo, como he mencionado antes, el límite de tiempo de ejecución para el programa es de 6 segundos y debe ser capaz de manejar grandes wordcadenas (> 1000 caracteres). Aquí es un ejemplo de una entrada grande.

Aquí está mi código:

1) el método real:

entrada: una cadena wordy un String []book

salida: el número de formas palabra se puede escribir solamente el uso de cadenas en el libro

public static int optimal(String word, String[] book){
    int count = 0;

    List<List<String>> allCombinations = allSubstrings(word);

    List<String> empty = new ArrayList<>();

    List<String> wordList = Arrays.asList(book);

    for (int i = 0; i < allCombinations.size(); i++) {

        allCombinations.get(i).retainAll(wordList);

        if (!sumUp(allCombinations.get(i), word)) {
            allCombinations.remove(i);
            allCombinations.add(i, empty);
        }
        else count++;
    }

    return count;
}

2) allSubstrings ():

entrada: una cadena input

salida: una lista de listas, cada una conteniendo una combinación de subcadenas que se suman a la entrada

static List<List<String>> allSubstrings(String input) {

    if (input.length() == 1) return Collections.singletonList(Collections.singletonList(input));

    List<List<String>> result = new ArrayList<>();

    for (List<String> temp : allSubstrings(input.substring(1))) {

        List<String> firstList = new ArrayList<>(temp);
        firstList.set(0, input.charAt(0) + firstList.get(0));
        if (input.startsWith(firstList.get(0), 0)) result.add(firstList);

        List<String> l = new ArrayList<>(temp);
        l.add(0, input.substring(0, 1));
        if (input.startsWith(l.get(0), 0)) result.add(l);
    }

    return result;
}

3.) sumup ():

entrada: una lista de cadenas inputy una cadenaexpected

salida: true si los elementos de inputañadir hastaexpected

public static boolean sumUp (List<String> input, String expected) {

    String x = "";

    for (int i = 0; i < input.size(); i++) {
        x = x + input.get(i);
    }
    if (expected.equals(x)) return true;
    return false;
}
Andy Turner:

He descubierto lo que estaba haciendo mal en mi respuesta anterior : no estaba usando memoization, así que estaba rehaciendo una gran cantidad de trabajo innecesario.

Considere un conjunto de libros {"a", "aa", "aaa"}, y una palabra objetivo "aaa". Hay cuatro formas de construir este objetivo:

"a" + "a" + "a"
"aa" + "a"
"a" + "aa"
"aaa"

Mi intento anterior tendría paseo a través de los cuatro, por separado. Pero en cambio, se puede observar que:

  • Hay 1 manera de construir "a"
  • Se puede construir "aa"de 2 maneras, ya sea "a" + "a"o usar "aa"directamente.
  • Puede construir "aaa"ya sea usando "aaa"directamente (1 manera); o "aa" + "a"(2 maneras, puesto que hay 2 maneras de construir "aa"); o "a" + "aa"(1 manera).

Tenga en cuenta que el tercer paso que aquí sólo se suma una sola cadena adicional para una cadena construida previamente, por lo que sabemos el número de formas en que se puede construir.

Esto sugiere que si contamos el número de formas en las que el prefijo wordse puede construir, se puede utilizar para calcular que trivialmente el número de formas prefijo mayor mediante la adición de uno más de la cadena book.

He definido una clase trie simple, por lo que puede buscar rápidamente los prefijos de las bookpalabras que coinciden en cualquier posición dada en word:

class TrieNode {
  boolean word;
  Map<Character, TrieNode> children = new HashMap<>();

  void add(String s, int i) {
    if (i == s.length()) {
      word = true;
    } else {
      children.computeIfAbsent(s.charAt(i), k -> new TrieNode()).add(s, i + 1);
    }
  }
}

Para cada uno en la carta s, esto crea una instancia de TrieNode, y almacena los TrieNodede los caracteres subsiguientes etc.

static long method(String word, String[] book) {
  // Construct a trie from all the words in book.
  TrieNode t = new TrieNode();
  for (String b : book) {
    t.add(b, 0);
  }

  // Construct an array to memoize the number of ways to construct
  // prefixes of a given length: result[i] is the number of ways to
  // construct a prefix of length i.
  long[] result = new long[word.length() + 1];

  // There is only 1 way to construct a prefix of length zero.
  result[0] = 1;

  for (int m = 0; m < word.length(); ++m) {
    if (result[m] == 0) {
      // If there are no ways to construct a prefix of this length,
      // then just skip it.
      continue;
    }

    // Walk the trie, taking the branch which matches the character
    // of word at position (n + m).
    TrieNode tt = t;
    for (int n = 0; tt != null && n + m <= word.length(); ++n) {
      if (tt.word) {
        // We have reached the end of a word: we can reach a prefix
        // of length (n + m) from a prefix of length (m).
        // Increment the number of ways to reach (n+m) by the number
        // of ways to reach (m).
        // (Increment, because there may be other ways).
        result[n + m] += result[m];
        if (n + m == word.length()) {
          break;
        } 
      }
      tt = tt.children.get(word.charAt(n + m));
    }
  }

  // The number of ways to reach a prefix of length (word.length())
  // is now stored in the last element of the array.
  return result[word.length()];
}

Para el muy largo aportaciones hechas por los OP , esto da salida:

$ time java Ideone

2217093120

real    0m0.126s
user    0m0.146s
sys 0m0.036s

Un poco más rápido que el requerido 6 segundos - y esto incluye el tiempo de inicio de JVM también.


Editar : de hecho, el trie no es necesario. Usted puede simplemente reemplazar el bucle de "Walk trie" con:

for (String b : book) {
  if (word.regionMatches(m, b, 0, b.length())) {
    result[m + b.length()] += result[m];
  }
}

y realiza más lento, pero aún mucho más rápido que 6s:

2217093120

real    0m0.173s
user    0m0.226s
sys 0m0.033s

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=99338&siteId=1
Recomendado
Clasificación