Is Subsequence
Ti vengono date due stringhe, s e t. Restituisci true se puoi trasformare t in s eliminando alcune delle sue lettere (eventualmente nessuna), mantenendo l’ordine delle lettere rimanenti, e false altrimenti. Per esempio, ace è una sottosequenza di abcde, ma aec no.
Funzione
- sstring
- la stringa da cercare
- tstring
- la stringa da cui eliminare lettere
- Restituisceboolean
- vero se s può essere letta all'interno di t in ordine, eventualmente con spazi
Vincoli
1 ≤ s.length ≤ 3 × 1041 ≤ t.length ≤ 5 × 104setcontengono solo lettere minuscole inglesi.
Esempi
- Input
- s = "ace"t = "abcde"
- Output
- true
- Spiegazione
- Elimina
beddaabcdee rimaneace, nello stesso ordine.
- Input
- s = "aec"t = "abcde"
- Output
- false
- Spiegazione
tcontiene tutte e tre le lettere, ma l’unicacsi trova prima dell’unicae. Dopo aver usato laeall’indice 4, non rimane alcunacalla sua destra.
- Input
- s = "moon"t = "monsoon"
- Output
- true
- Spiegazione
- Usa la
mall'indice 0, leoagli indici 1 e 4 e lanall'indice 6 dimonsoon. Le lettere in mezzo vengono eliminate.
Per approfondire
Se t rimane fisso e confronti un milione di stringhe s con esso, come prepareresti t affinché ogni confronto sia più veloce che leggere tutto t?
Suggerimenti
Aprili uno alla volta. Ognuno rivela un po’ di più.
Guarda la prima lettera di
s. Quale occorrenza di essa intdovresti usare?Usa la copia più anticipata. Prenderne una successiva può solo lasciare meno
tper il resto dis, quindi la scelta più anticipata non è mai peggiore.Mantieni un indice in
se uno int. Scorrituna lettera alla volta, avanza l’indice insa ogni corrispondenza e verifica alla fine se ha raggiunto la fine dis.
Soluzione
Una sottosequenza può saltare lettere di t in qualsiasi punto, il che suggerisce di provare molte posizioni per s. Non è necessario: abbinare ogni lettera di s nel primo punto in cui può andare non è mai peggio di qualsiasi altra scelta, quindi basta una passata da sinistra a destra con due puntatori per deciderlo.
Programmazione dinamica sui prefissi
Corretto, ma non termina sui test più grandi
Intuizione
Indichiamo con dp[i][j] se le prime i lettere di s rientrano nelle prime j lettere di t. Vi rientrano se vi rientravano già senza t[j-1], oppure se s[i-1] è uguale a t[j-1] e le prime i-1 lettere rientrano in t[:j-1]. Il prefisso vuoto rientra ovunque e la riga i legge solo la riga i-1, quindi bastano due righe di lunghezza m+1.
La tabella riempie ogni cella: con s di 25,000 lettere e t di 50,000, sono 1.25 × 10^9 celle.
Algoritmo
- Crea una riga
prevdim+1valori, tuttitrue: unasvuota è compatibile con ogni prefisso dit. - Per ogni
ida 1 an, crea una rigacurconcur[0] = false. - Per ogni
jda 1 am, impostacur[j]sucur[j-1], oppure suprev[j-1]quandos[i-1]è uguale at[j-1]. - Sostituisci
prevconcur. - Restituisci
prev[m].
def isSubsequence(s, t):
n, m = len(s), len(t)
# prev[j]: the first i-1 letters of s fit inside t[:j]. An empty s fits anywhere.
prev = [True] * (m + 1)
for i in range(1, n + 1):
cur = [False] * (m + 1)
for j in range(1, m + 1):
cur[j] = cur[j - 1] or (s[i - 1] == t[j - 1] and prev[j - 1])
prev = cur
return prev[m]Due puntatori con corrispondenza greedy
Intuizione
Mantieni un puntatore i alla prossima lettera di s che ti serve e prendi la prima copia che incontri in t. Questa scelta è sicura. Se una disposizione valida usa una copia successiva di s[i], sostituirla con la copia più vicina all'inizio mantiene l'ordine e lascia più caratteri di t per il resto di s. Quindi, se esiste una disposizione, quella greedy la trova.
Il ciclo legge ogni lettera di t al massimo una volta e mantiene due indici: tempo O(n + m) e memoria O(1).
Algoritmo
- Imposta
i = 0persej = 0pert. - Finché entrambi gli indici sono all'interno delle rispettive stringhe, confronta
s[i]cont[j]. - Se sono uguali, incrementa
i. - Incrementa
jin ogni caso. - Restituisci se
iè uguale alla lunghezza dis.
def isSubsequence(s, t):
i = j = 0
while i < len(s) and j < len(t):
if s[i] == t[j]:
i += 1
j += 1
return i == len(s)
Trappole e casi limite
- Cercare ogni lettera di
sin un punto qualsiasi dit, invece che dopo la corrispondenza precedente. Questo accettaaecinabcde. - Usare due volte la stessa lettera.
noonnon è una sottosequenza dimoon: la sua unican, all'indice 3, non può essere sia la prima sia l'ultima lettera. - Restituire se
jha raggiunto la fine dit. Il ciclo spesso termina lì, indipendentemente dal fatto chessia stata trovata o meno; soloite lo dice. - Non fermarsi quando
tfinisce.abcconfrontato conabdeve restituirefalse. - Leggere
s[i]dopo cheiha raggiunto la fine dis. In Python o Java quella lettura genera un errore, quindi controllaiprima di confrontare.
Domande frequenti3
Qual è la complessità temporale di Is Subsequence?
La soluzione a due puntatori viene eseguita in tempo O(n + m), dove n e m sono le lunghezze di s e t, con memoria aggiuntiva O(1). Il ciclo si interrompe dopo al massimo m passaggi. La tabella dei prefissi richiede un tempo O(n × m).
In che modo Is Subsequence è correlato alla sottosequenza comune più lunga?
s è una sottosequenza di t esattamente quando la loro sottosequenza comune più lunga ha lunghezza n, ovvero la lunghezza di s. La tabella dei prefissi del primo approccio è quella che costruisci per quel problema. Qui devi solo sapere se tutta s trova posto, quindi è sufficiente una passata greedy.
Qual è la differenza tra una sottosequenza e una sottostringa?
Una sottostringa è un blocco di lettere consecutive, mentre una sottosequenza può saltare delle lettere, purché l’ordine rimanga lo stesso. ace è una sottosequenza di abcde, ma non una sua sottostringa. Ogni sottostringa è una sottosequenza, ma non vale il contrario.
Problemi simili
Problemi che usano le stesse idee. Risolverne due o tre è ciò che fissa uno schema.
def isSubsequence(s, t):
# Scrivi il codice quiTest #1
Test #2
Test #3
Argomenti
s = "ace" t = "abcde"
Output atteso
true