Questa probabilmente non è la soluzione più efficiente algoritmicamente, ma è pulita dal punto di vista del design di classe. Questa soluzione prende l'approccio di confrontare parole date "ordinate".
Possiamo dire che una parola è una permutazione di un altro se contiene le stesse lettere nello stesso numero. Ciò significa che è possibile convertire la parola da String
a Map<Character,Integer>
. Tale conversione avrà la complessità O (n) dove n è la lunghezza di String
, presupponendo che gli inserimenti nell'implementazione Map
costino O (1).
Il Map
conterrà come chiavi tutti i caratteri trovati nella parola e come valori le frequenze dei caratteri.
Esempio. abbc viene convertito in [a->1, b->2, c->1]
BACB viene convertito in [a->1, b->2, c->1]
Quindi, se avete sapere se due parole sono una permutazione dell'altro, entrambi è possibile convertire in mappe e quindi richiamare Map.equals
.
Quindi è necessario scorrere la stringa di testo e applicare la trasformazione a tutte le sottostringhe della stessa lunghezza delle parole che si stanno cercando.
Miglioramento proposto da Inerdial
Questo approccio può essere migliorata aggiornando la mappa in modo "rolling".
I.e. se si sta verificando l'abbinamento con l'indice i=3
nell'esempio haystack nell'OP (sottostringa xya
), la mappa sarà [a->1, x->1, y->1]
. Quando si avanza nel mucchio di fieno, diminuire il conteggio dei caratteri per e incrementare il conteggio per haystack[i+needle.length()]
.
(Dropping zeri per assicurarsi Map.equals()
opere, o semplicemente l'attuazione di un confronto personalizzato.)
miglioramento proposto da Max
E se anche noi introduciamo matchedCharactersCnt
variabile? All'inizio del pagliaio sarà 0
. Ogni volta che cambi la mappa verso il valore desiderato, aumenti la variabile. Ogni volta che lo si cambia lontano dal valore desiderato, si decrementa la variabile. Ogni iterazione controlla se la variabile è uguale alla lunghezza dell'ago. Se lo è, hai trovato una corrispondenza. Sarebbe più veloce di confrontare la mappa completa ogni volta.
Pseudocodice fornita da Max:
needle = "abbc"
text = "abbcbbabbcaabbca"
needleSize = needle.length()
//Map of needle character counts
targetMap = [a->1, b->2, c->1]
matchedLength = 0
curMap = [a->0, b->0, c->0]
//Initial map initialization
for (int i=0;i<needle.length();i++) {
if (curMap.contains(haystack[i])) {
matchedLength++
curMap[haystack[i]]++
}
}
if (matchedLength == needleSize) {
System.out.println("Match found at: 0");
}
//Search itself
for (int i=0;i<haystack.length()-needle.length();i++) {
int targetValue1 = targetMap[haystack[i]]; //Reading from hashmap, O(1)
int curValue1 = curMap[haystack[i]]; //Another read
//If we are removing beneficial character
if (targetValue1 > 0 && curValue1 > 0 && curValue1 <= targetValue1) {
matchedLength--;
}
curMap[haystack[i]] = curValue1 + 1; //Write to hashmap, O(1)
int targetValue2 = targetMap[haystack[i+needle.length()]] //Read
int curValue2 = curMap[haystack[i+needle.length()]] //Read
//We are adding a beneficial character
if (targetValue2 > 0 && curValue2 < targetValue2) { //If we don't need this letter at all, the amount of matched letters decreases
matchedLength++;
}
curMap[haystack[i+needle.length()]] = curValue2 + 1; //Write
if (matchedLength == needleSize) {
System.out.println("Match found at: "+(i+1));
}
}
//Basically with 4 reads and 2 writes which are
//independent of the size of the needle,
//we get to the maximal possible performance: O(n)
questa risposta sembra incompleta. Tu dici come intendi canonicalizzare la parola, ma non dire nulla sulla ricerca di permutazioni nel testo. Useresti la stessa idea dei poster 2? –
Se combinato con la seconda idea dell'OP, questo approccio può essere migliorato aggiornando la mappa in modo "scorrevole". Cioè se stai cercando l'indice 'i = 3' nel pagliaio di esempio nell'OP (sottostringa' xya'), la mappa sarà '[a-> 1, x-> 1, y-> 1]'. Quando avanza nel mucchio di fieno, decrementa il conteggio dei caratteri per 'pagliaio [i]', e aumenta il conteggio per 'pagliaio [i + ago.lungo()]'. (Eliminando gli zeri per assicurarsi che 'Map.equals()' funzioni, o semplicemente implementando un confronto personalizzato.) – millimoose
@Inerdial il tuo miglioramento è davvero elegante! Congratulazioni!! –