2011-09-29 19 views
7

Niedawno zapytano w trakcie wywiadu: "Jak znaleźć odwrót wszystkich ciągów znaków, jeśli istnieje na liście ponad milionów łańcuchów?"Pary ciągów w kolejności odwrotnej na liście ponad miliona ciągów znaków?

Dla Np [1] = "abc", muszę sprawdzić "cba" dokładnie, żadne anagramy.

Metoda 1. Przechowuj wszystkie sznurki w HashSet rozpocząć przechodzenie od pierwszego łańcucha i sprawdzić, czy istnieje w postaci odwróconej Hashset. jeśli tak, to powiązać innego przejść do następnego elementu.

Czy możesz zasugerować jakąkolwiek metodę, jeśli pamięć jest ograniczeniem?

+0

Na re - nie jest jasne, czy chcesz znaleźć wszystkie łańcuchy, które są rewersami innych osób na tej samej liście, czy też, biorąc pod uwagę ciąg, znaleźć ciąg na liście, który jest jego odwrotnością. Ten drugi, oczywiście, jest prostym problemem wyszukiwania, po odwróceniu podanego ciągu. –

+0

Chociaż zgadzam się z Danielem, biorąc pod uwagę PAMIĘĆ jako ograniczenie, nie ma to żadnego znaczenia. –

+0

@DanielRHicks Redagowałem moje pytanie .... on miał na myśli, że dla wszystkich ciągów na liście znajdź czy istnieje odwrotność tego ... –

Odpowiedz

1

Możesz użyć Bloom Filter, która powie ci, czy łańcuch znaków już istnieje w strukturze tablicy mieszającej, ale każdy z nich ma tylko 0 lub 1, więc używane jest bardzo mało miejsca.

dokładnie 1 000 000 bitów == 125 KB

+0

1.) zajmie to więcej pamięci. 2) nie potrzebujesz długiego łańcucha, aby uzyskać wiele z nich o tej samej długości. –

+0

Masz rację, zmienię odpowiedź. – Serdalis

+0

Odpowiedź Zmieniona. – Serdalis

4

Jeśli wolno, można na miejscu sort struny więc jeśli spojrzeć na odwrocie ciąg można zrobić wyszukiwania binarnego.

1

Najpierw ułożyłem łańcuchy za pomocą skrótu niezależnego od kierunku. Może to być prosta suma znaków, choć na pewno istnieją lepsze schematy, które miałyby mieszać z obu stron. I aby "osłodzić umowę" można dodać długość łańcucha do wartości mieszania lub w inny sposób włączyć ją do mieszania.

Następnie, gdy łańcuchy są podzielone na identyczne grupy skrótów, porównaj "długi układ".

Należy zauważyć, że używając tego schematu lub tego, w którym po prostu używasz skrótu zależnego od kierunku do przodu lub do tyłu, rzeczą, którą należy zrobić, jest nie wstawianie ciągu bezpośrednio do zestawu haszów, ale raczej sprawdzenie go (z odwróceniem hasz, jeśli jest to konieczne), a jeśli uzyskasz dopasowanie (i późniejsze długie porównywanie jest prawdziwe) usuń już zaszyfrowany ciąg i połącz te dwa. Drugi ciąg nigdy nie wchodzi w skład zestawu, a jeśli wszystkie ciągi mają co najwyżej pasujące liczby, to w zestawie skrótu znajduje się tylko 500 000 wpisów, a jeśli łańcuchy były losowe, prawdopodobnie bliższe 250 000 (nie siedziałem w dół, aby obliczyć prawdopodobieństwa).

Potrzebujesz więc tylko jednego przejścia przez zestaw łańcuchów, aby wykonać całą operację.

+0

wykonanie niezależnej od kierunku wartości hash nie daje żadnych rzeczywistych korzyści, ale z pewnością zwiększy współczynnik kolizji. –

+0

Hash niezależny od kierunku "" abc "i" cba "do tego samego zasobnika. To znacznie zmniejsza liczbę kombinacji, które musisz wypróbować. –

+0

Nie rozumiem. Dlaczego to wszystko obniża? O jakich kombinacjach mówisz? –

1

Z „ pamięci jako ograniczenie”, wtedy nie będę nawet pójść na HashSet (który AFAIK również usunąć zduplikowane ciągi na pierwotnej liście), ponieważ będziesz z wykorzystaniem dodatkowej struktury HashSet, który zajmuje trochę pamięci.

Sortowanie również nie poprawi użycia pamięci.

Chciałbym użyć oryginalnej listy (która już istnieje, więc nie będzie używana dodatkowa pamięć) + 3-bajtowa zmienna całkowita do iteracji listy. 3 bajty można iteracyjne nad listą 2^24 = 16777216 ciągi

z „pamięci jako ograniczenie” pójdę za 2 pętle. Myślę, że pseudokod C-jak będzie łatwiej zrozumieć, że mój zwykły angielski.

Uwagi:

  1. Z przykład podany w pytaniu, to nie jest to faktycznie Lista ale tablicą, więc będę pracować nad strukturą, jakby to był Array
  2. Pytanie nie jest wyjaśnić, czy sparować "abc", "def", "cba", "abc". Będę parować pierwsze "abc" z "cba", a także, że "cba" z "drugim" abc "(intencja nie jest jasne w pytaniu)
  3. Zakładam, że nie możemy zmienić oryginalnej listy

Oto najmniej kod pamięci zużycie mogę myśleć:

// "list" holds the original list (array) 
for (int i = 0; i < length(list) - 1; i++) { 
    for (int j = i + 1; j < length(list); j++) { 
     if (list[i] == reverse(list[j])) { 
      print(list[i] + " reversed is " list[j]) 
     } 
    } 
} 

chodzi o zużycie pamięci, rozwiązanie to zajmie 2 zmiennych całkowitych (zwykle 4 bajtów każdy) + pierwotnej liście, które zakładam, że my nie można się pozbyć.

Dotyczące użycia procesora e (właściwie nie ma znaczenia na podstawie pytania), liczba powtórzeń łańcuchów będzie następująca: (N * (N + 1))/2 gdzie N jest długością listy

+0

1 000 000 000 000 powtórzeń, mniej więcej. (Nie licząc rzeczywistej pętli porównania). –

+0

Hmm, nie. Tylko 1 iteracja na liście. Kolejność tego rozwiązania to N. Ale tak jak powiedziałem, a osoba, która zapytała wyraźnie, nie ma potrzeby robienia tego szybko, ale z najmniejszą ilością pamięci. Lista już tam jest, właśnie dodaję 3 bajty. Ile dodatkowych bajtów zajmuje twoje rozwiązanie? –

+0

Proszę wyjaśnić, jak w jednym przejściu listy identyfikujesz wszystkie odwrócone duplikaty na liście. –

1

Możesz wybrać HashTable i użyj segmentów, aby zmniejszyć konflikt mieszania. To, co musimy teraz zrobić dla konkretnego ciągu zapytań, to po prostu odwrócić go, zaszyfrować i znaleźć w HashTable zamiast przechodzić od początku do końca.

+0

Tak, to zasadniczo jest to samo co mój schemat, tylko z dwukrotnie większą ilością skrótów. –

1

To jus moja opinia:

Chciałbym utworzyć skrót z

klucz = charakteru

value = Lista ciąg które zaczynają z tego znaku

  • Teraz uruchom pętlę, w której musisz zacząć od pierwszego ciągu znaków.
  • odwrócić to
  • Weź pierwszą literę i szukać tego klucza w hash
  • następnie w wartości, że zawiera ona wykaz ciągów i znaleźć ciąg w tym wykazie
Powiązane problemy