O (1) ek alan kullanarak iki dizenin birbirinin permütasyonu olup olmadığını nasıl kontrol edebilirim?


13

İki dize verildiğinde, O (1) alanı kullanarak birbirlerinin permütasyonu olup olmadığını nasıl kontrol edebilirsiniz? Dizelerin değiştirilmesine hiçbir şekilde izin verilmez.
Not: O (1) hem dize uzunluğuna hem de alfabenin boyutuna göre boşluk.


3
Ne düşünüyorsun? Ne denedin ve nereye takıldın? Dizeler sabit boyutlu bir alfabenin üzerinde mi? Histogramlarını hesaplamayı denediniz mi?
Yuval Filmus

@YuvalFilmus hem ipin uzunluğuna hem de alfabenin boyutuna göre O (1) boşluk olmalıdır
Anonim

Bu açıkça imkansız görünüyor. Herhangi bir algoritma, bir dizede veya tek bir karakterde en az bir konum depolamak için ek alan gerektirir. Bunların hiçbiri O (1) değildir.
David Schwartz

@DavidSchwartz - nasıl? O (1) bir bute değil sabit anlamına gelir. Dizenin ne kadar uzun olduğu önemli değildir, içindeki konum bir sayıdır.
Davor

Makine modeline bağlıdır, tek tip modellerde sorun yoktur. Logaritmik bir maliyet modelinde indeksi depolamak O(log n), ne uzunluk ne de alfabe boyutuna göre sabit olmayan uzunluk n dizeleri içindir. Dizeler geçici olarak değiştirilebildiğinde, alfabetik boyutta doğrusal olan ancak logaritmik bir modelde dize uzunluğunda sabit olan artan alfabeli bir çözüm olduğunu düşünüyorum.
kap

Yanıtlar:


7

Saf yaklaşım, her iki dizgenin histogramlarını oluşturmak ve bunların aynı olup olmadığını kontrol etmek olacaktır. Bir geçişte hesaplanabilecek böyle bir veri yapısını (boyutu alfabenin boyutuyla doğrusal olacak) depolayamadığımızdan, her olası sembolün oluşumunu birbiri ardına saymamız gerekir:

function count(letter, string)
    var count := 0
    foreach element in string
        if letter = element
            count++
    return count

function samePermutation(stringA, stringB)
    foreach s in alphabet
        if count(s, stringA) != count(s, stringB)
            return false
    return true

Bu, elbette, sayıların ve yineleyici indekslerinin, dizelerin uzunluğuna bağlı olmak yerine sabit boyutta tamsayılar olduğunu varsayar.


Bir optimizasyon olarak, bir dizinin üzerinden geçebilir ve yalnızca karşılaştığınız harflerin histogramlarını hesaplayabilirsiniz. Bu şekilde karmaşıklık alfabe boyutundan bağımsız hale gelir.
Yuval Filmus

@YuvalFilmus yorumunu genişletmek için ayrıca 1) dize uzunluklarının aynı olup olmadığını veya 2) her iki giriş dizesini de yinelediğini kontrol etmeniz gerekir. Bunlardan birine ihtiyacınız var, çünkü birindeki bazı harfler diğerinde değil. Seçenek 1 daha az hesaplamaya sahip olmalıdır.
BurnsBA

@YuvalFilmus Bu ikinci dereceden zaman karmaşıklığı anlamına geleceği için, alfabenin ortalama dize boyutundan daha küçük olmasını beklemekten kaçınmak istedim. Küçük dizeler ve sıralı bir alfabe için, iç döngüdeki sayımla birlikte bir sonraki en küçük simgeyi hesaplamayı düşünürüm, böylece bir alfabe döngüsünün birkaç yinelemesini atlayabilir - karmaşıklığı ile O(n * min(n, |Σ|)). Hm, şimdi düşündüğüme göre, cevabınızdaki "tekrar etmesine izin verilen" çözüm gibi görünüyor, değil mi?
Bergi

countdeğil O(1)(yani taşabilir)
reinierpost

1
@Eternalcode Hiç countbir int:-) olduğunu söylemedim Evet, işe yaramaz, ancak Java'da yine de gerçekleşemez
Bergi

12

Dizileri belirtin ve n uzunluğunda olduğunu varsayalım .A,Bn

İlk önce her dizideki değerlerin farklı olduğunu varsayalım. İşte alanı kullanan bir algoritma :O(1)

  1. Her iki dizinin de minimum değerlerini hesaplayın ve aynı olduklarını kontrol edin.

  2. Her iki dizinin ikinci minimum değerlerini hesaplayın ve aynı olduklarını kontrol edin.

  3. Ve bunun gibi.

O(1)k(k+1)k

Öğelerin tekrarlamasına izin verildiğinde, algoritmayı aşağıdaki gibi değiştiririz:

  1. mA,1,mB,1mA,1=mB,1

  2. mA,2,mB,2mA,1,mB,1mA,2=mB,2

  3. Ve bunun gibi.


1
O(n2)O(1)

4
O(lgn)O(1)

7
Sayma (logaritmik) alana ihtiyaç duyar, ancak - alan kullanımının bu tanımıyla - dizi üzerinde yineleme bile yapar. Dolayısıyla, alan kullanımının katı anlamı altında, bunu sabit alanda yapmanın bir yolu yoktur.
Daniel Jour

4
@DanielJour, kullandığınız maliyet modeline bağlıdır . Düzgün maliyet altında, bu sabit alanda mümkündür.
ryan

7
Yalnızca sabit sayıda bite izin verilirse, yalnızca sabit boyutlu alfabeleri işleyebilirsiniz (bu, normal diller teorisinden gelir).
Yuval Filmus

2

C karakterini benzersiz bir asal sayı (a = 2, b = 3, c = 5 vb.) İle eşleştiren f (c) fonksiyonunu tanımlayın.

set checksum = 1
set count = 0 <-- this is probably not even necessary, but it's another level of check
for character c in string 1
    checksum = checksum * f(c)
    count = count + 1
for character c in string 2
    checksum = checksum / f(c)
    count = count = 1

permutation = count == 0 and checksum == 1

O(1)


f(c)O(1)O(1)

Gönderdikten sonra fark ettiğim bir diğer konu, sağlama toplamının büyük dizeler için O (1) alan gereksinimini ihlal edebileceği ölçüde devasa bir sayı olacağıdır. Bu, kayan nokta kullanarak ve bir dizede bir karakter tarafından sonradan diğerine bölünerek ve sonra sağlama toplamının 1'e yakın olması gerektiğini söyleyerek çözülebilir.
Alex Stasse

4
O(logn)

4
Θ(n)n

0

Bunu yapabilirsiniz O(nlogn). İki dizeyi sıralayın ve dizini dizine göre karşılaştırın. Eğer herhangi bir yerde farklılık gösterirlerse, birbirlerinin permütasyonu değildirler.

Bir O(n)çözelti için, karma kullanılabilir. Bu karma işlevi işe yarayacaktır ve eherhangi bir harf için ascii değeri olacaktır. Dizelerin iki karması farklıysa, birbirlerinin permütasyonu değildir.

Bağlantıdaki karma işlevi:

Potansiyel bir aday da bu olabilir. Tek bir tamsayı R'yi düzeltin. Karma yapmak istediğiniz her öğe e için faktörü (R + 2 * e) hesaplayın. Sonra tüm bu faktörlerin çarpımını hesaplayın. Son olarak hash elde etmek için ürünü 2'ye bölün.

(R + 2e) 'deki faktör 2, tüm faktörlerin garip olduğunu garanti eder, böylece ürünün hiç 0 olmasını engeller. .

Örneğin, R = 1779033703'ü seçiyorum. Bu, rastgele bir seçimdir, bazı deneyler yapmak, belirli bir R'nin iyi veya kötü olduğunu göstermelidir. Değerlerinizin [1, 10, 3, 18] olduğunu varsayın. Ürün (32-bit ints kullanılarak hesaplanır)

(R + 2) * (R + 20) * (R + 6) * (R + 36) = 3376724311 Bu nedenle karma olur

3376724311/2 = 1688362155.

R değerini değiştirerek çift karma kullanmak (veya fazla doldurmak için) bunları çok yüksek olasılıklı permütasyonlar olarak tanımlayacaktır .


1
Dizeleri değiştirmenize izin verilmediğinden sıralayamazsınız. Karma gelince, yanlış cevap verebilecek rastgele bir algoritmadır.
Yuval Filmus

0

Diyelim ki s ve t adında iki dizeniz var.

Eşitsiz olmadıklarından emin olmak için sezgisel tarama kullanabilirsiniz.

  1. s.length == t.length
  2. s karakterlerinin toplamı = = t cinsinden karakterlerin toplamı
  3. [2'deki ile aynı, ancak toplam yerine xor ile]

Bundan sonra, dizenin eşit olduğunu kanıtlamak için kolayca bir algoritma çalıştırabilirsiniz.

  1. bir dizeyi diğerine eşit olacak şekilde sıralayın ve karşılaştırın (O (n ^ 2))
  2. her ikisini de sırala ve karşılaştır (O (2n günlüğü (n))
  3. Her iki dizede de aynı miktarlar olup olmadığını s cinsinden her karakter için kontrol edin (O (n ^ 2))

Tabii ki ek alan kullanma izniniz yoksa bu kadar hızlı sıralayamazsınız. Bu nedenle, hangi algoritmayı seçtiğinizin önemi yoktur - her algoritmanın, yalnızca O (1) boşluk olduğunda ve buluşsal yöntemlerin eşit olamayacaklarını kanıtlayamadığı durumlarda O (n ^ 2) zamanında çalışması gerekir.


3
" Dizeleri değiştirmeye hiçbir şekilde izin verilmez. "
Bergi

0

Tüm rutin için C stili kodda:

for (int i = 0; i < n; i++) {
   int k = -1;
   next: for (int j = 0; j <= i; j++)
       if (A[j] == A[i]) {
          while (++k < n)
              if (B[k] == A[i])
                  continue next;
          return false; // note at this point j == i
       }
}
return true; 

Veya çok ayrıntılı sahte kodda (1 tabanlı indeksleme kullanarak)

// our loop invariant is that B contains a permutation of the letters
// in A[1]..A[i-1]
for i=1..n
   if !checkLetters(A, B, i)
      return false
return true

Burada checkLetters (A, B, i) işlevi, A [1] 'de A [i]' nin M kopyası varsa .. A [i], B'de A [i] 'nin en az M kopyası olup olmadığını kontrol eder:

checkLetters(A,B,i)
    k = 0 // scan index into B
    for j=1..i
      if A[j] = A[i]
         k = findNextValue(B, k+1, A[i])
         if k > n
            return false
    return true

findNextValue işlevi B'de bir dizinden başlayarak bir değer arar ve bulunduğu dizini döndürür (veya bulunamazsa n + 1).

n2


Lütfen C kodunuzu sözde koda çevirebilir misiniz? Bu bir programlama sitesi değil.
Yuval Filmus

Bu, Bergi'nin cevabının başka bir varyantı gibi görünüyor (bazı önemsiz farklılıklar ile).
Yuval Filmus

O(nm)O(n2)

0

O(n3n

Döngü aracılığıyla string1ve string2her karakter kontrolü için ne sıklıkta bulunabilir string1ve string2. Ben bir karakter bir dizede diğerinden daha sık, ben bir permütasyon değil. Tüm karakterlerin frekansları eşitse, dizeler birbirinin permütasyonudur.

İşte bunu kesinleştirmek için bir parça piton

s1="abcaba"
s2="aadbba"

def check_if_permutations(string1, string2):
  for string in [string1, string2]:
    # string references string1 
    #  string2, it is not a copy
    for char in string:
      count1=0
      for char1 in string1:
        if  char==char1:
          count1+=1
      count2=0
      for char2 in string2:
        if  char==char2:
          count2+=1
      if count1!=count2:
        print('unbalanced character',char)
        return()
  print ("permutations")
  return()

check_if_permutations(s1,s2)

stringstring1string2charchar1char2O(logn)count1count2string[string1, string2]

Tabii ki sayım değişkenlerine bile ihtiyacınız yok ama işaretçiler kullanabilirsiniz.

s1="abcaba"
s2="aadbba"

def check_if_permutations(string1, string2):
  for string in [string1, string2]:
    # string references one of string1 
    # or string2, it is not a copy
    for char in string:
      # p1 and p2 should be views as pointers
      p1=0
      p2=0
      while (p1<len(string1)) and (p2<len(string2)):
        # p1>=len(string1): p1 points to beyond end of string
        while (p1<len(string1)) and (string1[p1]!=char) :
          p1+=1
        while(p2<len(string2)) and (string2[p2]!=char):
          p2+=1
        if (p1<len(string1)) != (p2<len(string2)):
          print('unbalanced character',char)
          return()
        p1+=1
        p2+=1
  print ("permutations")
  return()

check_if_permutations(s1,s2)

O(log(n))

n


Bu, Bergi'nin aşağıdaki çözümü ile aynıdır.
Yuval Filmus

@YuvalFilmus Hayır, tüm alfabe üzerinde yineleme yapmaz ve bu nedenle çalışma süresi alfabe boyutuna bağlı değildir. Yalnızca test edilmesi gereken iki dizeyi kullanır. Ayrıca ikinci program saymayı önler.
miracle173

@YuvalFilmus Şimdi, sizin ve diğer yorumların programımda kullandığım yönü gösterdiğini görüyorum.
miracle173
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.