Jak znaleźć wszystkie niezrównoważone pareny w ciągu w czasie liniowym ze stałą pamięcią?


11

Podczas wywiadu dostałem następujący problem:

Daje ciąg znaków, który zawiera pewną mieszankę parenów (nie nawiasów klamrowych ani nawiasów klamrowych - tylko pareny) z innymi znakami alfanumerycznymi, zidentyfikuj wszystkie pareny, które nie mają pasujących paren.

Na przykład w ciągu „) (ab))” indeksy 0 i 5 zawierają pareny, które nie mają pasujących paren.

Przedstawiam działające rozwiązanie O (n) przy użyciu pamięci O (n), używając stosu i przechodząc przez ciąg po dodaniu parens do stosu i usunięciu ich ze stosu, ilekroć napotkałem zamykający paren i górną część stosu paren otwierający.

Następnie ankieter zauważył, że problem można rozwiązać w czasie liniowym ze stałą pamięcią (jak w, bez dodatkowego użycia pamięci poza tym, co jest pobierane przez dane wejściowe).

Zapytałem, jak i powiedziała coś o przejściu sznurka raz z lewej strony, identyfikując wszystkie otwarte pareny, a potem drugi raz z prawej, identyfikując wszystkie bliskie pareny ... a może było na odwrót. Naprawdę nie rozumiałem i nie chciałem prosić, żeby mnie za to trzymała.

Czy ktoś może wyjaśnić zaproponowane przez nią rozwiązanie?


1
Najpierw możemy potrzebować od ciebie wyjaśnienia. Czy pierwsze lub drugie pareny w „(()” są uważane za niezrównoważone? Czy ostatnie pareny lub drugie do ostatnich w „())” są uważane za niezrównoważone? A może wystarczy zidentyfikować dowolny zestaw parenów o najmniejszej liczności, tak aby ich usunięcie pozostawiło pozostałe parens w równowadze? Albo coś innego? Czy jest to ta część wywiadu, aby odpowiedź mogła po prostu przedstawić uzasadnioną specyfikację?
John L.,

Powiedziałbym, że to nie ma znaczenia, do ciebie. Usuń zestaw, który pozostawia resztę zrównoważoną.
tymczasowego

5
Następnie usuń je wszystkie; P
Veedrac

@ Veedrac, oczywiście (jak wiadomo) plakat zapomniał słowa „minimalna” w „Usuń dowolny minimalny zestaw…”.
LSpice 18.01.19

Sam nie „zapomniałem”, ale raczej to pominąłem, ponieważ nie wydawało mi się to ważną specyfikacją, ponieważ istnieje tylko jeden zestaw, który można usunąć, aby był zrównoważony, oprócz „wszystkich”, które oczywiście pokonuje cel ćwiczenia.
tymczasowego

Odpowiedzi:


17

O(1)Θ(log(n))n

Możesz zachować podstawową zasadę zastosowanego algorytmu. Straciłeś okazję do optymalizacji pamięci.

używając stosu i przechodząc przez ciąg po dodaniu parens do stosu i usuwając je ze stosu, ilekroć napotkałem zamykający paren, a góra stosu zawierała otwierający paren

Co zawiera ten stos? Nigdy nie będzie zawierać ()(nawias otwierający, a następnie nawias zamykający), ponieważ za każdym razem, gdy )pojawia się napis, (zamiast naciskać ). Tak więc stos ma zawsze formę )…)(…(- kilka nawiasów zamykających, a następnie kilka nawiasów otwierających.

Nie potrzebujesz stosu, aby to przedstawić. Zapamiętaj tylko liczbę nawiasów zamykających i liczbę nawiasów otwierających.

Jeśli przetwarzasz ciąg znaków od lewej do prawej, używając tych dwóch liczników, na końcu masz liczbę niedopasowanych nawiasów zamykających i liczbę niedopasowanych nawiasów otwierających.

Θ(n)

Podsumowując: przetwarzaj ciąg znaków od lewej do prawej. Zachowaj licznik niedopasowanych nawiasów otwierających. Jeśli widzisz otwierający nawias, zwiększ licznik. Jeśli widzisz zamykający nawias, a licznik jest niezerowy, zmniejsz licznik. Jeśli widzisz nawias zamykający, a licznik jest równy zero, wypisz bieżący indeks jako niedopasowany nawias zamykający.

Ostateczna wartość licznika to liczba niedopasowanych nawiasów otwierających, ale to nie określa ich pozycji. Zauważ, że problem jest symetryczny. Aby wyświetlić pozycje niedopasowanych nawiasów otwierających, po prostu uruchom algorytm w przeciwnym kierunku.

Ćwiczenie 1: zapisz to w formalnej notacji (matematyka, pseudokod lub twój ulubiony język programowania).

Ćwiczenie 2: przekonaj się, że jest to ten sam algorytm, co Apass.Jack , po prostu wyjaśniono inaczej.


Och, bardzo dobry Gilles, bardzo dobrze wyjaśniony. Teraz rozumiem doskonale. Minęło sporo lat, odkąd otrzymałem odpowiedź na jedno z moich pytań.
tymczasowego

„Jeśli chcesz zgłosić pozycje niedopasowanych nawiasów na końcu, musisz zapamiętać pozycję każdego nawiasu”. Nie do końca. Czas liniowy nie oznacza pojedynczego przejścia. Możesz zrobić drugie przejście, aby znaleźć nawiasy po niedopasowanej stronie i oznaczyć je.
Kucząca Kaczka

W ostatnim kroku nie musisz biegać w odwrotnej kolejności, możesz po prostu oznaczyć ostatnie N ”(„ jako niezgodność.
Kaczka Mooing

1
@MooingDuck To nie działa. Np (().
orlp

Chociaż bardzo podoba mi się ta odpowiedź, coś mnie w tym niepokoi. Chodzi o to, że „muszę w jakiś sposób zapamiętać pozycję. Myślę, że mam z tym problem: jak„ wypisać bieżący indeks ”bez zużywania pamięci (lub dość specyficzny kontekst, w którym twoje wyniki są konsumowane w taki sposób, że kolejność w-twoich wyników nie ma znaczenia)
Édouard

8

Ponieważ możemy po prostu zignorować wszystkie znaki alfanumeryczne, założymy, że odtąd ciąg zawiera tylko nawiasy. Podobnie jak w pytaniu, istnieje tylko jeden rodzaj nawiasu, „()”.

Jeśli będziemy kontynuować usuwanie zrównoważonych nawiasów, dopóki nie będzie można usunąć więcej zrównoważonych nawiasów, wszystkie pozostałe nawiasy muszą wyglądać jak „))…) ((… (”, które są niezbalansowanymi nawiasami. Ta obserwacja sugeruje, że powinniśmy najpierw znaleźć ten punkt zwrotny) , przed którymi mamy tylko niezrównoważone nawiasy zamykające, a po których mamy niezrównoważone tylko nawiasy otwierające.

Oto algorytm. Krótko mówiąc, najpierw oblicza punkt zwrotny. Następnie generuje dodatkowy nawias zamykający, skanując ciąg od początku do prawej do punktu zwrotnego. Symetrycznie generuje dodatkowy nawias otwierający, skanując od końca do lewej do punktu zwrotnego.


Pozwolić strn

Zainicjuj turning_point=0, maximum_count=0, count=0. Dla każdego iz 0aby n-1wykonać następujące czynności.

  1. Jeśli str[i] = ')'dodaj 1 do count; w przeciwnym razie odejmij 1.
  2. Jeśli count > maximum_count , ustaw turning_point=ii maximum_count=count.

Teraz turning_pointjest indeks punktu zwrotnego.

Zresetować maximum_count=0, count=0. Dla każdegoi z 0aby turning_pointwykonać następujące czynności.

  1. Jeśli str[i] = ')'dodaj 1 do count; w przeciwnym razie odejmij 1.
  2. Jeśli count > maximum_count, ustawmaximum_count = count . Dane wyjściowe ijako indeks niezrównoważonego nawiasu zamykającego.

Zresetować maximum_count=0, count=0. Dla każdego izn-1 do turning_point+1dołu wykonaj następujące czynności.

  1. Jeśli str[j] = '('dodaj 1 do count; w przeciwnym razie odejmij 1.
  2. Jeśli count > maximum_count, ustaw maximum_count = count. Wyniki jako indeks niezrównoważonego nawiasu otwierającego.

O(n)O(1)O(u)u


Jeśli przeanalizujemy powyższy algorytm, zobaczymy, że tak naprawdę wcale nie musimy znajdować punktu zwrotnego i korzystać z niego. Ładna obserwacja, że ​​wszystkie niezrównoważone nawiasy zamykające mają miejsce, zanim wszystkie niezrównoważone nawiasy otwierające mogą zostać zignorowane, chociaż interesujące.

Oto kod w Pythonie .

Wystarczy nacisnąć „Uruchom”, aby zobaczyć kilka wyników testu.


Ćwiczenie 1. Pokaż, że powyższy algorytm wyświetli zestaw nawiasów o najmniejszej liczności, tak aby pozostałe nawiasy były zrównoważone.

Problem 1. Czy możemy uogólnić algorytm na przypadek, gdy ciąg zawiera dwa rodzaje nawiasów, takie jak „() []”? Musimy ustalić, jak rozpoznać i potraktować nową sytuację, przypadek przeplatania, „([)]”.


Lol, ćwiczenie 1 i problem 1, słodkie. Logika opisanego algorytmu jest zaskakująco trudna do wyobrażenia. Musiałbym to kodować jutro, żeby to zdobyć.
tymczasowego

Wygląda na to, że przegapiłem raczej oczywiste, ale najważniejsze wyjaśnienie. Logika jest w rzeczywistości bardzo prosta. Najpierw wypisujemy każdy dodatkowy nawias otwierający. Po przekroczeniu punktu zwrotnego generujemy każdy dodatkowy nawias zamykający. Gotowe.
John L.,

Znalezienie niezrównoważonych nawiasów otwierających jest nieprawidłowe. To znaczy, jeśli twój arr to „())”, p wynosi 2, a p + 1 wypada poza granicę arr. Tylko pomysł - aby znaleźć niezrównoważone nawiasy otwierające, możesz odwrócić arr i użyć części algorytmu, aby znaleźć niezrównoważone nawiasy zamykające (oczywiście z odwrotnie dostosowanymi indeksami).
OzrenTkalcecKrznaric

p+1

Trochę mnie to zrozumiało, ale podoba mi się to, jest całkiem sprytne .. i działa przynajmniej w każdym przypadku, o którym myślałem
dquijada
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.