Bir dizideki tüm dengesiz parenleri sabit bellekle doğrusal zamanda nasıl bulabilirsiniz?


11

Bir röportaj sırasında bana şu problem verildi:

Diğer alfasayısal karakterlerle bazı parantez karışımları (parantez veya parantez değil, yalnızca parantez) içeren bir dize verir, eşleşen parantezi olmayan tüm parantezleri tanımlar.

Örneğin, ") (ab))" dizgisinde, 0 ve 5 dizinleri eşleşen paren içermeyen parenler içerir.

O (n) bellek kullanarak, bir yığın kullanarak ve bir kez yığına parens ekleyerek ve bir kapanış paren ve içerdiği yığının üstüyle karşılaştığımda bunları yığından kaldırarak çalışan bir O (n) çözümü ortaya koydum bir açılış paren.

Daha sonra görüşmeci, sorunun sabit bellekle lineer zamanda çözülebileceğini belirtti (girişte olduğu gibi, girdi tarafından alınanların dışında ek bellek kullanımı yok).

Nasıl sordum ve o dize bir kez tüm açık parens tanımlayan soldan bir şey söyledi ve daha sonra sağdan tüm yakın parens tanımlayan ikinci kez .... ya da belki de başka bir yol oldu. Gerçekten anlamadım ve ondan beni tutmasını istemedim.

Önerdiği çözümü açıklığa kavuşturan var mı?


1
Önce sizden bazı açıklamalara ihtiyacımız olabilir. "())" Daki ilk ebeveynler veya ikinci ebeveynler dengesiz kabul ediliyor mu? Yoksa en az kardinalitesi olan herhangi bir ebeveyn grubunu tanımlamak, onları kaldırmanın kalan ebeveynleri dengeli bırakmasını sağlayacak kadar mı? Veya başka bir şey? Yoksa bir cevap sadece haklı bir şartname ortaya koyabilmek için mülakatın bir parçası mı?
John L.

Senin için önemli olmadığını söyleyebilirim. Kalanı dengede bırakan tüm setleri çıkarın.
temporary_user_name

5
Sonra hepsini kaldırın; P
Veedrac

@Veedrac, elbette (bildiğiniz gibi) poster, "Herhangi bir minimal seti kaldır … " daki "minimal" kelimesini unuttu .
LSpice

Ben "unutmadım", ama bunu daha çok dışarıda bıraktım, çünkü benim için önemli bir şartname gibi görünmüyordu çünkü "hepsini" yanında dengeli hale getirmek için kaldırılabilecek tek bir set var. tabii ki egzersizin amacını yenmektir.
temporary_user_name

Yanıtlar:


17

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

Kullandığınız algoritmanın temel prensibini koruyabilirsiniz. Bellek optimizasyonu fırsatını kaçırdınız.

bir yığın kullanarak ve dizeye bir kez yığına parens ekleyerek ve bir kapanış pareniyle karşılaştığımda ve yığının üstünde bir açılış paren içerdiğinde yığıntan kaldırılıyor

Peki bu yığın ne içeriyor? Asla ihtiva edecek ()her beri (kapanan parantez ardından bir parantez açma) )görünen, sizin pop (yerine iterek ). Bu nedenle yığın her zaman formdadır )…)(…(- bir grup kapanış parantezi ve ardından bir grup açılış parantezidir.

Bunu temsil etmek için yığına ihtiyacınız yok. Sadece kapanış parantez sayısını ve açılış parantez sayısını unutmayın.

Bu iki sayacı kullanarak dizeyi soldan sağa işlerseniz, sonunda sahip olduğunuz şey, uyumsuz kapanış parantezlerinin sayısı ve uyumsuz açılış parantezlerinin sayısıdır.

Θ(n)

Özetle: dizeyi soldan sağa doğru işleyin. Eşsiz açılış parantezlerinin bir sayacını koruyun. Bir açılış parantezi görürseniz, sayacı artırın. Bir kapanış parantezi görürseniz ve sayaç sıfırdan farklıysa, sayacı azaltın. Bir kapanış parantezi görürseniz ve sayaç sıfırsa, geçerli dizini eşleşmeyen bir kapanış parantezi olarak girin.

Sayacın son değeri, uyumsuz açılış parantezlerinin sayısıdır, ancak bu size konumlarını vermez. Sorunun simetrik olduğuna dikkat edin. Uyuşmayan açılış parantezlerinin konumlarını listelemek için algoritmayı ters yönde çalıştırmanız yeterlidir.

Alıştırma 1: Bunu resmi bir gösterimde (matematik, sözde kod veya en sevdiğiniz programlama dili) yazın.

Alıştırma 2: Bunun Apass.Jack ile aynı algoritma olduğuna kendiniz ikna edin , sadece farklı şekilde açıklayın .


Oh çok iyi Gilles, çok iyi açıkladı. Şimdi mükemmel anlıyorum. Sorularımdan birinde sizden bir yanıt aldığımdan bu yana birkaç yıl geçti.
temporary_user_name

"Sonunda uyumsuz parantezlerin konumlarını bildirmek istiyorsanız, her parantezin konumunu hatırlamanız gerekir." Pek değil. Doğrusal zaman tek geçiş anlamına gelmez. Eşleşmeyen taraftaki köşeli parantezleri bulmak ve işaretlemek için ikinci bir geçiş yapabilirsiniz.
Mooing Duck

Son adım için, tersine çalıştırmak zorunda değilsiniz, son N "(" uyumsuzlukları olarak işaretleyebilirsiniz.
Mooing Duck

1
@MooingDuck Bu işe yaramıyor. Örn (().
orlp

Bu cevabı gerçekten sevmeme rağmen, bir şey beni rahatsız ediyor. Bir şey "bir şekilde pozisyonu hatırlamam gerekiyor Ve bence onunla ilgili sorun: nasıl" geçerli endeksi çıktı "bellek tüketmeden (veya çıkışları öyle bir şekilde tüketildiği oldukça özel bir bağlam) çıktılarınızın siparişi önemli değil)
Édouard

8

Tüm alfasayısal karakterleri yok sayabileceğimiz için, dizenin bundan böyle yalnızca parantez içerdiğini varsayacağız. Soruda olduğu gibi, sadece bir tür parantez vardır ("()".

Dengeli parantez kaldırılıncaya kadar dengeli parantezleri kaldırmaya devam edersek, geriye kalan tüm parantezler "))…) () (hepsi dengesiz parantez olan) gibi görünmelidir. daha önce sadece dengesiz kapanış parantezlerimiz var ve bundan sonra sadece dengesiz açılış parantezlerimiz var.

İşte algoritma. Özetle, önce dönüm noktasını hesaplar. Daha sonra, dizeyi başlangıçtan sağa dönme noktasına kadar tarayarak ekstra kapanış parantezi çıkarır. Simetrik olarak, uçtan sola dönme noktasına kadar tarayarak ekstra açılış parantezi çıkarır.


strn

Başlat turning_point=0, maximum_count=0, count=0. Her biri için igelen 0için n-1aşağıdakileri yapın.

  1. Eğer str[i] = ')', 1 count; aksi takdirde 1 çıkarın.
  2. Eğer count > maximum_count, turning_point=ive öğelerini ayarlayın maximum_count=count.

Şimdi turning_pointdönüm noktasının endeksi.

Sıfırla maximum_count=0, count=0. Her biri için igelen 0için turning_pointaşağıdakileri yapın.

  1. Eğer str[i] = ')', 1 count; aksi takdirde 1 çıkarın.
  2. Varsa count > maximum_countayarlayın maximum_count = count. iDengesiz bir kapanış parantezinin dizini olarak çıktı .

Sıfırla maximum_count=0, count=0. Her biri için igelen n-1için turning_point+1aşağıya aşağıdakileri yapın.

  1. Eğer str[j] = '(', 1 count; aksi takdirde 1 çıkarın.
  2. Varsa count > maximum_countayarlayın maximum_count = count. iDengesiz bir açılış parantezinin indeksi olarak çıktı .

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


Yukarıdaki algoritmayı analiz edersek, aslında dönüm noktasını bulmamız ve kullanmamız gerekmediğini göreceğiz. Tüm dengesiz kapanış parantezlerinin tüm dengesiz açılış parantezlerinin ilginç olmasına rağmen göz ardı edilmesinden önce gerçekleştiği iyi bir gözlemdir.

İşte Python'daki kod .

Birkaç test sonucunu görmek için "run" düğmesine basmanız yeterli.


Alıştırma 1. Yukarıdaki algoritmanın en az kardinaliteye sahip bir dizi parantez çıkacağını gösterin, böylece kalan parantezler dengelenir.

Sorun 1. Dizenin "() []" gibi iki tür parantez içerdiği durumlarda algoritmayı duruma göre genelleştirebilir miyiz? Yeni durumu, serpiştirme vakasını "([)]" nasıl tanıyacağımızı ve tedavi edeceğimizi belirlemeliyiz.


Lol, egzersiz 1 ve problem 1, sevimli. Açıkladığınız algoritmanın mantığını görmek şaşırtıcı derecede zordur. Bunu almak için yarın kodlamam gerekecekti.
temporary_user_name

Görünüşe göre oldukça açık ama en önemli açıklamayı kaçırdım. Mantık aslında çok basit. İlk olarak, her ekstra açılış parantezini çıkarıyoruz. Dönüm noktasını geçtikten sonra, her ilave kapanış parantezini çıkarırız. Bitti.
John

Dengesiz açılış parantezlerini bulmak yanlış. Yani eğer arr'niz "())" ise, p 2'dir ve p + 1 arr sınırının dışında kalır. Sadece bir fikir - dengesiz açılış parantezlerini bulmak için arr tersine çevirebilir ve dengesiz kapanış parantezlerini bulmak için algoritmanın bir kısmını kullanabilirsiniz (elbette, tersine uyarlanmış dizinlerle).
OzrenTkalcecKrznaric

p+1

Bunu anlamak için bana biraz aldı, ama hoşuma gitti, oldukça zeki .. ve en azından düşündüğüm her durumda çalışıyor
dquijada
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.