คุณจะหา parens ที่ไม่สมดุลทั้งหมดในสตริงในเวลาเชิงเส้นด้วยหน่วยความจำคงที่ได้อย่างไร?


11

ฉันได้รับปัญหาต่อไปนี้ระหว่างการสัมภาษณ์:

ให้สตริงที่มีส่วนผสมของ parens (ไม่ใช่วงเล็บเหลี่ยมหรือวงเล็บปีกกา - เพียง parens) กับอักขระตัวอักษรผสมตัวเลขอื่น ๆ ระบุ parens ทั้งหมดที่ไม่มี paren ที่ตรงกัน

ตัวอย่างเช่นในสตริง ") (ab))" ดัชนี 0 และ 5 มี parens ที่ไม่มี paren ที่ตรงกัน

ฉันหยิบยกวิธีการแก้ปัญหา O (n) ที่ทำงานโดยใช้หน่วยความจำ O (n) โดยใช้สแต็คและผ่านสตริงเมื่อเพิ่ม parens ให้กับสแต็กและลบออกจากสแต็กเมื่อใดก็ตามที่ฉันพบ paren ปิดและด้านบนของสแตก การเปิด paren

หลังจากนั้นผู้สัมภาษณ์ตั้งข้อสังเกตว่าปัญหาสามารถแก้ไขได้ในเวลาเชิงเส้นด้วยหน่วยความจำคงที่ (ในไม่มีการใช้หน่วยความจำเพิ่มเติมนอกเหนือจากสิ่งที่เกิดขึ้นจากการป้อนข้อมูล)

ฉันถามว่าเธอพูดอะไรบางอย่างเกี่ยวกับการเดินผ่านสตริงหนึ่งครั้งจากด้านซ้ายระบุ parens ที่เปิดอยู่ทั้งหมดและจากนั้นอีกครั้งที่สองจากด้านขวาที่ระบุ parens ทั้งหมดที่อยู่ใกล้ ... หรืออาจเป็นวิธีอื่น ฉันไม่เข้าใจและไม่อยากให้เธอจับฉันไว้

ทุกคนสามารถอธิบายโซลูชันที่เธอแนะนำได้หรือไม่


1
เราอาจต้องการคำชี้แจงจากคุณก่อน parens ตัวแรกหรือ parens ตัวที่สองใน "(()" ถือว่าไม่สมดุลหรือไม่ parens ตัวสุดท้ายหรือ parens ตัวที่สองถึง parens ตัวสุดท้ายใน "())" ถือว่าไม่สมดุลหรือไม่ หรือมันเพียงพอที่จะระบุชุดของ parens ใด ๆ ที่มีความสำคัญเชิงลบน้อยที่สุดเช่นการลบออกจะทำให้ parens ที่เหลืออยู่มีความสมดุล? หรืออย่างอื่น? หรือเป็นส่วนหนึ่งของการสัมภาษณ์นี้เพื่อที่ว่าคำตอบจะสามารถส่งต่อสเปคที่สมเหตุสมผลได้บ้าง?
John L.

ฉันจะบอกว่ามันไม่สำคัญขึ้นอยู่กับคุณ ลบชุดใด ๆ ที่ทำให้ส่วนที่เหลือมีความสมดุล
temporary_user_name

5
จากนั้นให้ลบออกทั้งหมด P
Veedrac

@Veedrac แน่นอน (เท่าที่คุณรู้) โปสเตอร์ลืมคำว่า "น้อยที่สุด" ใน "ลบชุดขั้นต่ำใด ๆ…."
LSpice

ฉันไม่ได้ "ลืมมัน" ต่อ se แต่แทนที่จะทิ้งไว้เพราะมันดูไม่เหมือนสเปคที่สำคัญสำหรับฉันเนื่องจากมีเพียงชุดเดียวที่สามารถลบออกได้เพื่อให้สมดุลนอกเหนือจาก "พวกเขาทั้งหมด" ซึ่ง แน่นอนว่าการเอาชนะจุดประสงค์ของการฝึก
temporary_user_name

คำตอบ:


17

เนื่องจากสิ่งนี้มาจากพื้นหลังการเขียนโปรแกรมและไม่ใช่แบบฝึกหัดวิทยาศาสตร์คอมพิวเตอร์เชิงทฤษฎีฉันคิดว่ามันใช้หน่วยความจำO(1)เพื่อจัดเก็บดัชนีลงในสตริง ในวิทยาการคอมพิวเตอร์เชิงทฤษฎีนี่หมายถึงการใช้โมเดลแรม ด้วยเครื่องจักรทัวริงที่คุณไม่สามารถทำเช่นนี้และคุณจะต้องΘ(เข้าสู่ระบบ(n))หน่วยความจำในการจัดเก็บดัชนีเป็นสตริงของความยาวnn

คุณสามารถรักษาหลักการพื้นฐานของอัลกอริทึมที่คุณใช้ คุณพลาดโอกาสในการเพิ่มประสิทธิภาพหน่วยความจำ

ใช้สแต็คและผ่านสตริงเมื่อเพิ่ม parens ไปยังสแต็กและลบออกจากสแต็กเมื่อใดก็ตามที่ฉันพบ paren ปิดและด้านบนของสแต็กมี paren เปิดอยู่

ดังนั้นสแต็กนี้มีอะไรบ้าง มันไม่เคยไปประกอบด้วย()(วงเล็บเปิดตามด้วยวงเล็บปิด) ตั้งแต่เมื่อ)ปรากฏคุณปรากฏแทนการผลักดัน( )ดังนั้นสแต็กจะอยู่ในรูปแบบเสมอ)…)(…(- วงเล็บปิดปิดหนึ่งอันตามด้วยวงเล็บเปิด

คุณไม่จำเป็นต้องใช้สแต็กเพื่อแสดงสิ่งนี้ เพียงจำจำนวนวงเล็บปิดและจำนวนวงเล็บเปิด

หากคุณประมวลผลสตริงจากซ้ายไปขวาใช้สองตัวนับเหล่านี้สิ่งที่คุณมีในตอนท้ายคือจำนวนวงเล็บปิดที่ไม่ตรงกันและจำนวนวงเล็บเปิดที่ไม่ตรงกัน

หากคุณต้องการรายงานตำแหน่งของวงเล็บที่ไม่ตรงกันในตอนท้ายคุณจะต้องจำตำแหน่งของวงเล็บแต่ละตัว นั่นจะต้องใช้หน่วยความจำΘ(n)ในกรณีที่แย่ที่สุด แต่คุณไม่จำเป็นต้องรอจนกว่าจะเสร็จสิ้นเพื่อสร้างผลผลิต ทันทีที่คุณพบวงเล็บปิดที่ไม่ตรงกันคุณจะรู้ว่ามันไม่ตรงกันดังนั้นให้เอาท์พุททันที แล้วคุณจะไม่ใช้จำนวนวงเล็บปิดที่ไม่ตรงกันสำหรับสิ่งใด ๆ ดังนั้นเพียงแค่เก็บรักษาวงเล็บเปิดที่ไม่มีใครเทียบ

โดยสรุป: ประมวลผลสตริงจากซ้ายไปขวา รักษาตัวนับของวงเล็บเปิดที่ไม่ตรงกัน หากคุณเห็นวงเล็บเปิดให้เพิ่มตัวนับ หากคุณเห็นวงเล็บปิดและตัวนับเป็นค่าที่ไม่ใช่ศูนย์ให้ลดจำนวนตัวนับ หากคุณเห็นวงเล็บปิดและตัวนับเป็นศูนย์ให้ส่งออกดัชนีปัจจุบันเป็นวงเล็บปิดที่ไม่ตรงกัน

ค่าสุดท้ายของตัวนับคือจำนวนวงเล็บเปิดที่ไม่ตรงกัน แต่สิ่งนี้ไม่ได้ให้ตำแหน่งของคุณ ขอให้สังเกตว่าปัญหาคือสมมาตร หากต้องการแสดงรายการตำแหน่งของวงเล็บเปิดที่ไม่ตรงกันให้เรียกใช้อัลกอริทึมในทิศทางตรงกันข้าม

แบบฝึกหัดที่ 1: เขียนสิ่งนี้ลงในสัญลักษณ์อย่างเป็นทางการ (คณิตศาสตร์, pseudocode หรือภาษาโปรแกรมที่คุณโปรดปราน)

แบบฝึกหัดที่ 2: โน้มน้าวตัวเองว่านี่เป็นอัลกอริธึมเดียวกับApass.Jackเพียงอธิบายต่างกัน


โอ้ Gilles ดีมากอธิบายได้ดีมาก ฉันเข้าใจอย่างสมบูรณ์แล้ว หลายปีมาแล้วที่ฉันได้รับคำตอบจากคุณในคำถามของฉัน
temporary_user_name

"ถ้าคุณต้องการรายงานตำแหน่งของวงเล็บที่ไม่ตรงกันในตอนท้ายคุณจะต้องจำตำแหน่งของวงเล็บแต่ละตัว" ไม่มาก เวลาเชิงเส้นไม่ได้หมายถึงการผ่านครั้งเดียว คุณสามารถทำครั้งที่สองเพื่อค้นหาวงเล็บเหลี่ยมในด้านที่ไม่ตรงกันและทำเครื่องหมายไว้
Mooing Duck

สำหรับขั้นตอนสุดท้ายคุณไม่จำเป็นต้องเรียกใช้ในสิ่งที่ตรงกันข้ามคุณสามารถทำเครื่องหมาย N สุดท้าย "(" เป็นคำที่ไม่ตรงกัน
Mooing Duck

1
@MooingDuck ไม่ทำงาน (()เช่น
orlp

ในขณะที่ฉันชอบคำตอบนี้จริงๆมีบางสิ่งที่รบกวนฉันเกี่ยวกับเรื่องนี้ นั่นคือ "ฉันต้องจำตำแหน่งและฉันคิดว่าปัญหาที่ฉันมีคือ: คุณจะ" ส่งออกดัชนีปัจจุบัน "ได้อย่างไรโดยไม่ต้องใช้หน่วยความจำมาก (หรือบริบทที่ค่อนข้างเฉพาะเจาะจง คำสั่งซื้อที่ได้จากผลผลิตของคุณไม่สำคัญ)
Édouard

8

เนื่องจากเราสามารถละเว้นอักขระที่เป็นตัวอักษรและตัวเลขทั้งหมดเราจะถือว่าสตริงมีเฉพาะวงเล็บต่อจากนี้ ดังเช่นในคำถามมีวงเล็บชนิดเดียวเท่านั้น "()"

ถ้าเราเอาวงเล็บที่มีสมดุลออกไปจนกว่าจะไม่สามารถเอาวงเล็บที่มีความสมดุลได้ออกไปวงเล็บที่เหลือทั้งหมดจะต้องมีลักษณะเหมือน ")) ... ) ((... (" ซึ่งเป็นวงเล็บที่ไม่สมดุลทั้งหมด) การสังเกตนี้แสดงให้เห็นว่า ก่อนหน้านี้เรามีวงเล็บปิดที่ไม่สมดุลเท่านั้นและหลังจากนั้นเรามีวงเล็บเปิดที่ไม่สมดุลเท่านั้น

นี่คืออัลกอริทึม สรุปคำนวณจุดเปลี่ยนก่อน จากนั้นจะแสดงผลลัพธ์เป็นวงเล็บปิดพิเศษสแกนสตริงตั้งแต่เริ่มต้นไปทางขวาจนถึงจุดเปลี่ยน สมมาตรมันส่งออกวงเล็บเปิดพิเศษสแกนจากปลายด้านซ้ายจนกระทั่งจุดเปลี่ยน


strn

turning_point=0, maximum_count=0, count=0เริ่มต้น สำหรับแต่ละiจากที่0ต้องn-1ทำดังต่อไปนี้

  1. ถ้าstr[i] = ')'เพิ่ม 1 ถึงcount; มิฉะนั้นให้ลบ 1
  2. หากcount > maximum_countชุดและturning_point=imaximum_count=count

ตอนนี้turning_pointเป็นดัชนีของจุดเปลี่ยน

maximum_count=0, count=0ตั้งค่าใหม่ สำหรับแต่ละiจากที่0ต้องturning_pointทำดังต่อไปนี้

  1. ถ้าstr[i] = ')'เพิ่ม 1 ถึงcount; มิฉะนั้นให้ลบ 1
  2. หากชุดcount > maximum_count maximum_count = countเอาต์พุตiเป็นดัชนีของวงเล็บปิดที่ไม่สมดุล

maximum_count=0, count=0ตั้งค่าใหม่ สำหรับแต่ละiจากn-1การturning_point+1ลงทำดังต่อไปนี้

  1. ถ้าstr[j] = '('เพิ่ม 1 ถึงcount; มิฉะนั้นให้ลบ 1
  2. หากชุดcount > maximum_count maximum_count = countเอาต์พุตiเป็นดัชนีของวงเล็บเปิดที่ไม่สมดุล

O(n)O(1)O(ยู)ยู


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

นี่คือรหัสในหลาม

เพียงกด "เรียกใช้" เพื่อดูผลการทดสอบหลายรายการ


แบบฝึกหัดที่ 1แสดงให้เห็นว่าอัลกอริธึมด้านบนจะแสดงชุดของวงเล็บที่มีความสำคัญเชิงลบน้อยที่สุดเพื่อให้วงเล็บที่เหลืออยู่นั้นสมดุล

ปัญหา 1.เราสามารถพูดคุยกับอัลกอริทึมทั่วไปในกรณีที่สตริงมีวงเล็บสองชนิดเช่น "() []" เราต้องกำหนดวิธีการรับรู้และปฏิบัติต่อสถานการณ์ใหม่กรณีการสอดแทรก "([)]"


ฮ่า ๆ ออกกำลังกาย 1 ปัญหา 1 น่ารัก ตรรกะของอัลกอริทึมที่คุณอธิบายยากที่จะมองเห็นได้ ฉันจะต้องใช้รหัสนี้ในวันพรุ่งนี้เพื่อรับมัน
temporary_user_name

ดูเหมือนว่าฉันพลาดคำอธิบายที่ชัดเจน แต่สำคัญที่สุด ในความเป็นจริงแล้วตรรกะนั้นง่ายมาก อันดับแรกเราแสดงผลวงเล็บเปิดพิเศษแต่ละอัน เมื่อเราผ่านจุดเปลี่ยนเราจะส่งออกวงเล็บปิดพิเศษแต่ละอัน เสร็จสิ้น
John L.

การค้นหาวงเล็บเปิดที่ไม่สมดุลนั้นไม่ถูกต้อง คือถ้า arr ของคุณคือ "())", p คือ 2 และ p + 1 อยู่นอกขอบเขต arr แค่แนวคิด - เพื่อค้นหาวงเล็บเปิดที่ไม่สมดุลคุณสามารถย้อนกลับ arr และใช้ส่วนหนึ่งของอัลกอริทึมเพื่อค้นหาวงเล็บปิดที่ไม่สมดุล (แน่นอนพร้อมกับดัชนีที่ปรับเปลี่ยนได้แบบย้อนกลับ)
OzrenTkalcecKrznaric

พี+1

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