Dada es una cadena word
y una matriz de cadenas book
que contiene algunas cadenas. El programa debe dar a conocer el número de posibilidades para crear word
só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 word
es 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 word
cadenas (> 1000 caracteres). Aquí es un ejemplo de una entrada grande.
Aquí está mi código:
1) el método real:
entrada: una cadena word
y 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 input
y una cadenaexpected
salida: true si los elementos de input
añ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;
}
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 word
se 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 book
palabras 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 TrieNode
de 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