Bagaimana cara memeriksa apakah dua string adalah permutasi satu sama lain menggunakan O (1) ruang tambahan?

13

Diberikan dua string bagaimana Anda bisa memeriksa apakah mereka permutasi satu sama lain menggunakan ruang O (1)? Memodifikasi string tidak diizinkan dengan cara apa pun.
Catatan: O (1) spasi dalam kaitannya dengan panjang string DAN ukuran alfabet.

Anonim
sumber
3
Bagaimana menurut anda? Apa yang sudah Anda coba, dan di mana Anda terjebak? Apakah string lebih dari alfabet berukuran konstan? Sudahkah Anda mencoba menghitung histogramnya?
Yuval Filmus
@YuvalFilmus itu harus O (1) ruang baik untuk panjang string dan ukuran alfabet
Anonim
Tampaknya ini mustahil. Algoritma apa pun akan membutuhkan ruang tambahan untuk menyimpan setidaknya posisi dalam satu string atau karakter tunggal. Tak satu pun dari hal-hal ini adalah O (1).
David Schwartz
@ Davidvidchwartz - bagaimana? O (1) berarti konstan, bukan satu bute. Tidak peduli berapa lama string itu, posisi di dalamnya adalah satu angka.
Davor
Itu tergantung pada model mesin, jelas tidak ada masalah dalam model seragam. Dalam model biaya logaritmik, menyimpan indeks adalah O(log n)untuk string dengan panjang n yang tidak konstan dengan panjang atau ukuran alfabet. Ketika string dapat dimodifikasi sementara, saya pikir ada solusi dengan peningkatan alfabet yang linier dalam ukuran alfabet tetapi konstan dalam panjang string dalam model logaritmik.
kap

Jawaban:

7

Pendekatan naif akan membangun histogram dari kedua string dan memeriksa apakah mereka sama. Karena kami tidak diperbolehkan menyimpan struktur data seperti itu (yang ukurannya akan linier dengan ukuran alfabet) yang dapat dihitung dalam satu lintasan, kami perlu menghitung kemunculan setiap simbol yang mungkin setelah yang lainnya:

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

Ini tentu saja mengasumsikan bahwa jumlah dan indeks iterator adalah bilangan bulat dari ukuran konstan, bukannya tergantung pada panjang string.

Bergi
sumber
Sebagai pengoptimalan, Anda dapat melihat satu array dan hanya menghitung histogram dari huruf yang Anda temui. Dengan cara ini kompleksitasnya menjadi independen dari ukuran alfabet.
Yuval Filmus
Untuk memperluas komentar @YuvalFilmus, Anda harus juga 1) memeriksa bahwa panjang string sama atau 2) iterate atas kedua string input. Anda memerlukan salah satu dari ini karena mungkin beberapa surat dalam satu tidak di yang lain. Opsi 1 harus memiliki perhitungan lebih sedikit.
BurnsBA
@YuvalFilmus Saya ingin menghindari itu karena itu berarti kompleksitas waktu kuadratik, saya berharap alfabet lebih kecil dari ukuran string rata-rata. Untuk string kecil dan alfabet berurutan, saya akan mempertimbangkan menghitung simbol hadiah terkecil berikutnya bersama dengan hitungan di loop dalam, sehingga orang dapat melewati beberapa iterasi loop alfabet - dengan kompleksitas O(n * min(n, |Σ|)). Hm, sekarang saya memikirkannya, itu terdengar seperti solusi "diizinkan untuk mengulangi" dari jawaban Anda, bukan?
Bergi
counttidak O(1)(yaitu mungkin meluap)
reinierpost
1
@Eternalcode Saya tidak pernah mengatakan bahwa countadalah int:-) Ya, itu tidak akan bekerja, tetapi di Jawa yang tidak dapat terjadi pula
Bergi
12

Nyatakan array oleh , dan anggap panjangnya n .A,Bn

Anggaplah pertama bahwa nilai-nilai dalam setiap array berbeda. Berikut ini adalah algoritma yang digunakan :O(1)

  1. Hitung nilai minimum kedua array, dan periksa apakah keduanya sama.

  2. Hitung nilai minimum kedua dari kedua array, dan periksa apakah keduanya sama.

  3. Dan seterusnya.

Menghitung nilai minimum array jelas menggunakan ruang . Dengan elemen terkecil ke k , kita dapat menemukan ( k + 1O(1)k dengan menemukan nilai minimal lebih besar darielemen terkecil ke- k (di sini kita menggunakan fakta bahwa semua elemen berbeda).(k+1)k

Saat elemen diizinkan untuk diulang, kami memodifikasi algoritme sebagai berikut:

  1. Hitung nilai minimum dari kedua array, hitung berapa kali masing-masing muncul, dan verifikasi m A , 1 = m BmA,1,mB,1 dan hitungnya sama.mA,1=mB,1

  2. Hitung nilai minimum lebih besar dari m A , 1 , m B , 1 dalam dua array (masing-masing), dan hitung berapa kali masing-masing muncul. Verifikasi bahwa m A , 2 = m B ,mA,2,mB,2mA,1,mB,1 , dan bahwa penghitungannya identik.mA,2=mB,2

  3. Dan seterusnya.

Yuval Filmus
sumber
1
Apakah pendekatan ini menjadi karena sepertinya satu-satunya cara untuk menemukan elemen min di ruang O ( 1 ) dan akses read-only ke array adalah untuk beralih ke semua elemen? O(n2)O(1)
ryan
4
Ini membutuhkan pemesanan pada alfabet, meskipun mudah untuk mengubah algoritma untuk tidak mengharuskan itu. Namun, dalam kasus "memiliki duplikat", ini membutuhkan ruang , bukan O ( 1 ) . Menghitung membutuhkan ruang. O(lgn)O(1)
Derek Elkins meninggalkan SE
7
Menghitung memang membutuhkan ruang (logaritmik), tetapi - dengan definisi penggunaan ruang ini - demikian juga iterasi pada array. Jadi, di bawah makna ketat dari penggunaan ruang, tidak ada cara untuk melakukannya di ruang konstan.
Daniel Jour
4
@DanielJour, itu tergantung pada model biaya yang Anda gunakan. Di bawah biaya seragam, ini dimungkinkan dalam ruang konstan.
ryan
7
Jika Anda hanya diizinkan jumlah bit konstan, Anda hanya dapat menangani huruf ukuran konstan (ini mengikuti teori bahasa reguler).
Yuval Filmus
2

Tentukan beberapa fungsi f (c) yang memetakan beberapa karakter c ke bilangan prima unik (a = 2, b = 3, c = 5, dll).

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

Hanya menyatakan bahwa Anda dapat menggunakan fungsi pemetaan bilangan prima agak mudah, dan kemungkinan besar di mana masalah akan muncul menjaga ruang.O(1)

Alex Stasse
sumber
Dengan terikat pada alfabet, harus menggunakan spasi O ( 1 ) , kalau tidak saya percaya itu tidak akan menjadi ruang konstan. Selain itu, jika Anda menghitungnya dalam ruang O ( 1 ) itu akan sangat tidak efisien berdasarkan hasil saat ini . Namun, +1 untuk pendekatan primality. f(c)O(1)O(1)
ryan
Masalah lain yang saya sadari setelah posting adalah bahwa checksum akan menjadi angka raksasa untuk string besar, sejauh itu dengan sendirinya bisa melanggar persyaratan ruang O (1). Ini dapat diatasi dengan menggunakan float dan mutliplying oleh karakter pada satu string kemudian membaginya pada yang lain, kemudian hanya mengatakan checksum harus dekat dengan 1. String harus benar-benar raksasa untuk kesalahan floating point menjadi masalah.
Alex Stasse
4
Jawaban seperti itu adalah alasan kita perlu berhati-hati terhadap model perhitungan kita. Model yang biasa kita gunakan ketika menganalisis algoritma menghitung memori dalam satuan kata-kata mesin , yang memiliki ukuran bit. Jadi, Anda tidak dapat melakukan perhitungan dalam bilangan bulat. Jika Anda beralih ke floating point, algoritme Anda mungkin gagal bahkan ketika kedua string adalah permutasi satu sama lain, dan sebaliknya tidak akan selalu memberikan jawaban yang benar ketika mereka tidak. O(logn)
Yuval Filmus
4
Ini tidak menggunakan ruang konstan. Bahkan untuk alfabet tetap, ukuran checksum bilangan bulat akan menjadi bit untuk input panjang n . Θ(n)n
David Richerby
0

Anda bisa melakukan ini O(nlogn) . Sortir kedua string, dan bandingkan indeksnya dengan indeks. Jika mereka berbeda di mana saja, mereka tidak permutasi satu sama lain.

Untuk O(n)solusi, hashing dapat digunakan. Fungsi hashing ini akan bekerja, dan e untuk huruf apa pun akan menjadi nilai ascii-nya. Jika dua hash string berbeda, mereka tidak permutasi satu sama lain.

Fungsi hashing dalam tautan:

Satu kandidat potensial mungkin adalah ini. Memperbaiki bilangan bulat ganjil R. Untuk setiap elemen e Anda ingin hash menghitung faktor (R + 2 * e). Kemudian hitung produk dari semua faktor ini. Akhirnya bagi produk dengan 2 untuk mendapatkan hash.

Faktor 2 dalam (R + 2e) menjamin bahwa semua faktor aneh, maka menghindari bahwa produk akan menjadi 0. Pembagian dengan 2 pada akhirnya adalah karena produk akan selalu aneh, maka pembagian hanya menghilangkan sedikit konstan .

Misalnya saya memilih R = 1779033703. Ini adalah pilihan sewenang-wenang, melakukan beberapa percobaan harus menunjukkan apakah R yang diberikan baik atau buruk. Asumsikan nilainya adalah [1, 10, 3, 18]. Produk (dihitung menggunakan int 32-bit) adalah

(R + 2) * (R + 20) * (R + 6) * (R + 36) = 3376724311 Maka hash akan

3376724311/2 = 1688362155.

Menggunakan hashing ganda (atau untuk lebih banyak lagi) dengan mengubah nilai R akan berhasil mengidentifikasi mereka sebagai permutasi dengan probabilitas yang sangat tinggi .

Dalam keraguan
sumber
1
Anda tidak dapat mengurutkan string karena Anda tidak diizinkan untuk memodifikasinya. Adapun hashing, itu adalah algoritma acak yang bisa memberikan jawaban yang salah.
Yuval Filmus
0

Katakanlah Anda memiliki dua string yang disebut s dan t.

Anda dapat menggunakan heuristik untuk memastikan bahwa mereka tidak sama.

  1. s.length == t.length
  2. jumlah karakter s == jumlah karakter dalam t
  3. [sama seperti pada 2. tetapi dengan xor, bukan jumlah]

Setelah ini, Anda dapat dengan mudah menjalankan algoritma untuk membuktikan bahwa stringnya sama.

  1. urutkan satu string agar sama dengan yang lain dan bandingkan (O (n ^ 2))
  2. urutkan keduanya dan bandingkan (O (2n log (n))
  3. periksa setiap karakter dalam s jika ada jumlah yang sama di kedua string (O (n ^ 2))

Tentu saja Anda tidak dapat mengurutkan secepat itu jika Anda tidak diizinkan untuk menggunakan ruang tambahan. Jadi tidak masalah algoritma mana yang Anda pilih - setiap algoritma akan membutuhkan akan berjalan dalam waktu O (n ^ 2) ketika hanya ada O (1) ruang dan jika heuristik tidak dapat membuktikan bahwa mereka tidak bisa sama.

MurksVomOrk
sumber
3
" Memodifikasi senar tidak diizinkan dengan cara apa pun. "
Bergi
0

Dalam kode C-style untuk seluruh rutin:

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; 

Atau dalam kode pseudo yang sangat verbose (menggunakan pengindeksan berbasis 1)

// 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

di mana fungsi checkLetters (A, B, i) memeriksa bahwa jika ada salinan M dari A [i] di A [1] .. A [i], maka setidaknya ada M salinan A [i] di B:

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

dan fungsi findNextValue mencari dalam B untuk nilai yang dimulai dari indeks, dan mengembalikan indeks yang ditemukannya (atau n +1 jika tidak ditemukan).

n2

MotiN
sumber
Bisakah Anda mengonversi kode C Anda menjadi pseudocode? Ini bukan situs pemrograman.
Yuval Filmus
Ini sepertinya varian lain dari jawaban Bergi (dengan beberapa perbedaan yang tidak penting).
Yuval Filmus
O(nm)O(n2)
0

O(n3n

Loop through string1dan string2, untuk setiap karakter, periksa seberapa sering dapat ditemukan di string1dan string2. Saya seorang karakter lebih sering dalam satu string daripada yang lain, itu bukan permutasi. Jika frekuensi semua karakter sama maka string adalah permutasi satu sama lain.

Berikut adalah sepotong python untuk membuat ini tepat

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] .

Tentu saja Anda bahkan tidak perlu variabel jumlah tetapi dapat menggunakan pointer.

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)) variabel untuk memegang nilai-nilai hitung.

n

keajaiban173
sumber
Ini sama dengan solusi Bergi di bawah ini.
Yuval Filmus
@YuvalFilmus Tidak, itu tidak mengulangi seluruh alfabet dan oleh karena itu runtime tidak tergantung pada ukuran alfabet. Hanya menggunakan dua string yang harus diuji. Juga program kedua menghindari penghitungan.
miracle173
@YuvalFilmus saya lihat sekarang, bahwa komentar Anda dan lainnya menunjuk ke arah yang saya gunakan dalam program saya.
miracle173