วิธีการตรวจสอบว่าสองสายเป็นพีชคณิตของกันและกันโดยใช้พื้นที่เพิ่มเติม O (1)?


13

ให้สองสายคุณจะตรวจสอบได้อย่างไรว่าพวกมันเป็นการเปลี่ยนแปลงซึ่งกันและกันโดยใช้พื้นที่ O (1)? ไม่อนุญาตให้ดัดแปลงสตริงใด ๆ
หมายเหตุ: พื้นที่ O (1) สัมพันธ์กับทั้งความยาวสตริงและขนาดของตัวอักษร


3
คุณคิดอย่างไร? คุณลองทำอะไรและคุณติดอยู่ที่ไหน สายอักขระมีตัวอักษรขนาดคงที่หรือไม่? คุณลองคำนวณฮิสโตแกรมของพวกเขาหรือไม่?
Yuval Filmus

@YuvalFilmus ควรเป็นช่องว่าง O (1) ทั้งความยาวของสตริงและขนาดของตัวอักษร
Anonymous

ดูเหมือนว่าเป็นไปไม่ได้อย่างชัดเจน อัลกอริทึมใด ๆ จะต้องการพื้นที่เพิ่มเติมเพื่อจัดเก็บอย่างน้อยหนึ่งตำแหน่งในหนึ่งสตริงหรืออักขระเดียว สิ่งเหล่านี้ไม่เป็น O (1)
David Schwartz

@DavidSchwartz - อย่างไร O (1) หมายถึงค่าคงที่ไม่ใช่หนึ่งบับ มันไม่สำคัญว่าสายจะยาวแค่ไหนตำแหน่งนั้นคือหนึ่งหมายเลข
Davor

มันขึ้นอยู่กับรุ่นของเครื่องแน่นอนไม่มีปัญหาในรุ่นที่เหมือนกัน ในแบบจำลองต้นทุนลอการิทึมที่เก็บดัชนีนั้นใช้O(log n)สำหรับสตริงที่มีความยาว n ซึ่งไม่คงที่โดยใช้ความยาวหรือขนาดตัวอักษร เมื่อสายสามารถแก้ไขได้ชั่วคราวฉันคิดว่ามีวิธีแก้ปัญหาด้วยตัวอักษรที่เพิ่มขึ้นซึ่งเป็นเชิงเส้นในขนาดตัวอักษร แต่คงที่ในความยาวสตริงในรูปแบบลอการิทึม
kap

คำตอบ:


7

แนวทางไร้เดียงสาคือการสร้างฮิสโทแกรมของทั้งสองสตริงและตรวจสอบว่ามันเหมือนกันหรือไม่ เนื่องจากเราไม่ได้รับอนุญาตให้จัดเก็บโครงสร้างข้อมูลดังกล่าว (ซึ่งขนาดจะเป็นเส้นตรงกับขนาดของตัวอักษร) ที่สามารถคำนวณได้ในการส่งครั้งเดียวเราจึงต้องนับการเกิดขึ้นของสัญลักษณ์ที่เป็นไปได้หลังจากนั้น:

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

สิ่งนี้ทำแน่นอนสมมติว่าดัชนีการนับและตัววนซ้ำเป็นจำนวนเต็มขนาดคงที่แทนที่จะขึ้นอยู่กับความยาวของสตริง


ในการเพิ่มประสิทธิภาพคุณสามารถข้ามอาร์เรย์หนึ่งแถวและคำนวณฮิสโตแกรมของตัวอักษรที่คุณพบเท่านั้น ด้วยวิธีนี้ความซับซ้อนจะเป็นอิสระจากขนาดตัวอักษร
Yuval Filmus

หากต้องการขยายความคิดเห็น @YuvalFilmus คุณจะต้อง 1) ตรวจสอบว่าความยาวสตริงเหมือนกันหรือ 2) วนซ้ำทั้งสตริงอินพุต คุณต้องการหนึ่งในสิ่งเหล่านี้เนื่องจากมีความเป็นไปได้ที่ตัวอักษรบางตัวในอันหนึ่งไม่ได้อยู่ในที่อื่น ตัวเลือก 1 ควรมีการคำนวณน้อยลง
BurnsBA

@YuvalFilmus ฉันต้องการหลีกเลี่ยงเพราะมันหมายถึงความซับซ้อนของเวลากำลังสองฉันคาดหวังว่าตัวอักษรจะเล็กกว่าขนาดสตริงเฉลี่ย สำหรับสตริงขนาดเล็กและตัวอักษรสั่งฉันต้องการพิจารณาการคำนวณต่อไปสัญลักษณ์ปัจจุบันมีขนาดเล็กที่สุดพร้อมกับนับในวงชั้นใน, เพื่อให้เราสามารถข้ามซ้ำไม่กี่ของวงตัวอักษร - O(n * min(n, |Σ|))กับความซับซ้อนของ หืมตอนนี้ข้าคิดแล้วว่ามันฟังดูเหมือนโซลูชั่น "อนุญาตให้ทำซ้ำ" จากคำตอบของคุณใช่ไหม?
Bergi

countไม่ใช่O(1)(เช่นอาจล้น)
reinierpost

1
@Eternalcode ฉันไม่เคยบอกว่าcountเป็นint:-) ใช่มันจะไม่ทำงาน แต่ในชวาที่ไม่สามารถเกิดขึ้นได้อยู่แล้ว
Bergi

12

แสดงว่าอาร์เรย์โดย, Bและคิดว่าพวกเขามีความยาวnA,Bn

สมมติก่อนว่าค่าในแต่ละอาร์เรย์นั้นแตกต่างกัน นี่คืออัลกอริทึมที่ใช้พื้นที่ :O(1)

  1. คำนวณค่าต่ำสุดของทั้งสองอาร์เรย์และตรวจสอบว่าเหมือนกัน

  2. คำนวณค่าต่ำสุดที่สองของทั้งสองอาร์เรย์และตรวจสอบว่าเหมือนกัน

  3. และอื่น ๆ

การคำนวณค่าต่ำสุดของอาร์เรย์อย่างชัดเจนใช้พื้นที่ จากองค์ประกอบที่เล็กที่สุดของkเราสามารถหาองค์ประกอบที่เล็กที่สุด( k + 1 )โดยการหาค่าที่น้อยที่สุดที่ใหญ่กว่าองค์ประกอบที่เล็กที่สุดของk (ที่นี่เราใช้ความจริงที่ว่าองค์ประกอบทั้งหมดนั้นแตกต่างกัน)O(1)k(k+1)k

เมื่อองค์ประกอบได้รับอนุญาตให้ทำซ้ำเราจะปรับเปลี่ยนอัลกอริทึมดังนี้:

  1. คำนวณค่าต่ำสุดของทั้งสองอาร์เรย์นับจำนวนครั้งที่ปรากฏแต่ละครั้งและตรวจสอบm A , 1 = m B , 1และจำนวนนั้นเหมือนกันmA,1,mB,1mA,1=mB,1

  2. คำนวณค่าต่ำสุดใหญ่กว่าm A , 1 , m B , 1ในสองอาร์เรย์ (ตามลำดับ) และนับจำนวนครั้งที่ปรากฏแต่ละครั้ง ตรวจสอบว่าm A , 2 = m B , 2และจำนวนนั้นเหมือนกันmA,2,mB,2mA,1,mB,1mA,2=mB,2

  3. และอื่น ๆ


1
วิธีนี้จะเป็นเนื่องจากดูเหมือนจะเป็นวิธีเดียวในการค้นหาองค์ประกอบขั้นต่ำในพื้นที่O ( 1 )และการเข้าถึงอาร์เรย์แบบอ่านอย่างเดียวเพื่อวนซ้ำองค์ประกอบทั้งหมดหรือไม่ O(n2)O(1)
ryan

4
สิ่งนี้ต้องการการเรียงลำดับตัวอักษรแม้ว่าจะง่ายที่จะเปลี่ยนอัลกอริทึมที่ไม่ต้องการ อย่างไรก็ตามใน "มีรายการที่ซ้ำกัน" กรณีนี้ต้องใช้พื้นที่ไม่O ( 1 ) การนับใช้พื้นที่ O(lgn)O(1)
Derek Elkins ออกจาก SE

7
การนับต้องใช้พื้นที่ (ลอการิทึม) แต่ - โดยคำจำกัดความของการใช้พื้นที่ - ดังนั้นแม้จะวนซ้ำในอาร์เรย์ ดังนั้นภายใต้ความหมายที่เข้มงวดของการใช้พื้นที่จึงไม่มีทางทำในพื้นที่คงที่ได้
Daniel Jour

4
@DanielJour ขึ้นอยู่กับรุ่นของต้นทุนที่คุณใช้ ภายใต้ค่าใช้จ่ายสม่ำเสมอนี้เป็นไปได้ในพื้นที่คงที่
ไรอัน

7
หากคุณได้รับอนุญาตจำนวนบิตคงที่เท่านั้นคุณสามารถจัดการกับตัวอักษรขนาดคงที่เท่านั้น
Yuval Filmus

2

กำหนดฟังก์ชัน f (c) ที่จับคู่อักขระ c กับหมายเลขเฉพาะเฉพาะ (a = 2, b = 3, c = 5, ฯลฯ )

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)

อีกปัญหาที่ฉันรู้หลังจากโพสต์คือการตรวจสอบจะเป็นตัวเลขขนาดใหญ่สำหรับสตริงขนาดใหญ่เท่าที่ตัวมันเองอาจละเมิดข้อกำหนดพื้นที่ O (1) สิ่งนี้สามารถแก้ไขได้โดยการใช้ float และ mutliplying โดยตัวอักษรบนสตริงหนึ่งแล้วหารด้วยอีกอันจากนั้นก็บอกว่า checksum ต้องอยู่ใกล้กับ 1 สตริงนั้นจะต้องมีขนาดมหึมาอย่างแท้จริงสำหรับข้อผิดพลาดของ floating point
Alex Stasse

4
O(logn)

4
Θ(n)n

0

O(nlogn)คุณสามารถทำเช่นนี้คือ เรียงลำดับสองสตริงและเปรียบเทียบดัชนีตามดัชนี หากพวกเขาแตกต่างกันทุกที่พวกเขาไม่ได้เรียงสับเปลี่ยนกัน

สำหรับO(n)วิธีการแก้ปัญหาสามารถใช้ hashing ฟังก์ชั่นการแฮ็กนี้จะใช้งานได้และ eสำหรับจดหมายใด ๆ ก็จะมีค่า ascii หากแฮชของสตริงทั้งสองนั้นต่างกันพวกเขาจะไม่เปลี่ยนสับเปลี่ยนกัน

ฟังก์ชั่นคร่ำเครียดในลิงค์:

ผู้สมัครที่มีศักยภาพคนหนึ่งอาจเป็นแบบนี้ แก้ไขจำนวนเต็มคี่อาร์สำหรับแต่ละองค์ประกอบ e ที่คุณต้องการแฮชคำนวณปัจจัย (R + 2 * e) จากนั้นคำนวณผลิตภัณฑ์ของปัจจัยเหล่านี้ทั้งหมด ในที่สุดก็หารผลิตภัณฑ์ด้วย 2 เพื่อให้ได้แฮช

ปัจจัย 2 ใน (R + 2e) รับประกันได้ว่าปัจจัยทั้งหมดแปลกดังนั้นการหลีกเลี่ยงว่าผลิตภัณฑ์จะกลายเป็น 0 การหารด้วย 2 ในตอนท้ายคือเนื่องจากผลิตภัณฑ์จะเป็นเลขคี่เสมอดังนั้นการหารจะลบบิตคงที่เสมอ .

เช่นฉันเลือก R = 1779033703 นี่เป็นตัวเลือกโดยพลการทำการทดลองบางอย่างควรแสดงว่า R ที่กำหนดหรือไม่ดี สมมติว่าค่าของคุณคือ [1, 10, 3, 18] ผลิตภัณฑ์ (คำนวณโดยใช้ int 32- บิต) คือ

(R + 2) * (R + 20) * (R + 6) * (R + 36) = 3376724311 ดังนั้นแฮชจะเป็น

3376724311/2 = 1688362155

การใช้ hashing สองครั้ง (หรือมากกว่านั้น) โดยการเปลี่ยนค่าของ R จะประสบความสำเร็จในการระบุว่าเป็นพีชคณิตที่มีความน่าจะเป็นสูงมาก


1
คุณไม่สามารถเรียงลำดับสตริงได้เนื่องจากคุณไม่ได้รับอนุญาตให้แก้ไข สำหรับการแฮ็กมันเป็นอัลกอริทึมแบบสุ่มที่สามารถให้คำตอบที่ผิด
Yuval Filmus

0

สมมติว่าคุณมีสองสายที่เรียกว่า s และ t

คุณสามารถใช้ฮิวริสติกเพื่อให้แน่ใจว่ามันไม่เท่ากัน

  1. s.length == t.length
  2. ผลรวมของ chars of s == ผลรวมของ chars ใน t
  3. [เหมือนกับใน 2 แต่มี xor แทนผลรวม]

หลังจากนี้คุณสามารถเรียกใช้อัลกอริทึมเพื่อพิสูจน์ว่าสตริงมีค่าเท่ากัน

  1. จัดเรียงสตริงหนึ่งให้เท่ากับอีกสตริงหนึ่งและเปรียบเทียบ (O (n ^ 2))
  2. จัดเรียงทั้งสองและเปรียบเทียบ (O (2n บันทึก (n))
  3. ตรวจสอบถ่านแต่ละตัวถ้ามีจำนวนเท่ากันในทั้งสองสตริง (O (n ^ 2))

แน่นอนคุณไม่สามารถจัดเรียงอย่างรวดเร็วถ้าคุณไม่ได้รับอนุญาตให้ใช้พื้นที่เพิ่มเติม ดังนั้นไม่สำคัญว่าอัลกอริทึมใดที่คุณเลือก - อัลกอริทึมแต่ละอย่างจะต้องทำงานในเวลา O (n ^ 2) เมื่อมีพื้นที่ว่างเพียง O (1) และหากฮิวริสติกไม่สามารถพิสูจน์ได้ว่าพวกเขาไม่เท่ากัน


3
" ไม่อนุญาตให้ดัดแปลงสตริงใด ๆ "
Bergi

0

ในโค้ด C-style สำหรับรูทีนทั้งหมด:

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; 

หรือในรหัสหลอกอย่างละเอียด (ใช้การจัดทำดัชนีแบบ 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

โดยที่ฟังก์ชันตัวตรวจสอบตัวอักษร (A, B, i) ตรวจสอบว่ามีสำเนา M ของ A [i] ใน A [1] .. A [i] ดังนั้นมีอย่างน้อย M สำเนา A [i] ใน 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

และฟังก์ชั่น findNextValue ค้นหาใน B เพื่อหาค่าที่เริ่มต้นจากดัชนีและส่งคืนดัชนีที่พบ (หรือ n + 1 หากไม่พบ)

n2


คุณช่วยแปลงรหัส C เป็น pseudocode ได้ไหม นี่ไม่ใช่ไซต์โปรแกรมมิง
Yuval Filmus

ดูเหมือนจะเป็นคำตอบที่แตกต่างไปจากคำตอบของ Bergi (ซึ่งมีความแตกต่างเล็กน้อย)
Yuval Filmus

O(nm)O(n2)

0

O(n3n

ห่วงผ่านstring1และstring2สำหรับการตรวจสอบทุกตัวอักษรว่ามักจะสามารถพบได้ในและstring1 string2ฉันตัวละครมักจะอยู่ในสายหนึ่งมากกว่าอีกสายหนึ่งมันไม่ใช่การเปลี่ยนแปลง หากความถี่ของตัวละครทั้งหมดเท่ากันสตริงนั้นจะเรียงสับเปลี่ยนกัน

นี่คือส่วนหนึ่งของ python ที่จะทำให้แม่นยำ

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]

แน่นอนคุณไม่จำเป็นต้องใช้ตัวแปรนับ แต่สามารถใช้พอยน์เตอร์ได้

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


นี่เป็นวิธีเดียวกับวิธีการแก้ปัญหาของ Bergi ด้านล่าง
Yuval Filmus

@YuvalFilmus ไม่มันไม่ได้วนซ้ำไปมาทั้งตัวอักษรและดังนั้นขนาดของมันจึงไม่ขึ้นกับขนาดตัวอักษร ใช้เฉพาะสองสายที่ควรทดสอบ นอกจากนี้โปรแกรมที่สองหลีกเลี่ยงการนับ
miracle173

@YuvalFilmus ฉันเห็นแล้วว่าความคิดเห็นของคุณและอื่น ๆ ชี้ไปในทิศทางที่ฉันใช้ในโปรแกรมของฉัน
miracle173
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.