อัลกอริทึมการจับคู่สตริง k ไม่ตรงกันอย่างรวดเร็ว


10

ฉันกำลังมองหาอัลกอริทึมการจับคู่สตริง k-mismatch ที่รวดเร็ว กำหนดสตริงรูปแบบ P ของความยาว m และสตริงข้อความ T ของความยาว n, ฉันต้องการอัลกอริทึม (เชิงเส้นเวลา) ที่รวดเร็วเพื่อค้นหาตำแหน่งทั้งหมดที่ P ตรงกับสตริงย่อยของ T กับ k ไม่ตรงกันมากที่สุด สิ่งนี้แตกต่างจากปัญหาความแตกต่างของ k (ระยะทางแก้ไข) ไม่ตรงกันหมายถึงสตริงย่อยและรูปแบบมีตัวอักษรที่แตกต่างกันในตำแหน่ง k มากที่สุด ฉันต้องการเพียง k = 1 (มากที่สุด 1 ไม่ตรงกัน) ดังนั้นอัลกอริทึมที่รวดเร็วสำหรับกรณีเฉพาะของ k = 1 ก็จะเพียงพอเช่นกัน ขนาดตัวอักษรคือ 26 (ตัวพิมพ์เล็กและตัวพิมพ์เล็ก) ดังนั้นความต้องการพื้นที่ไม่ควรเติบโตเร็วเกินไปเมื่อเทียบกับขนาดของตัวอักษร (เช่นอัลกอริทึม FAAST ฉันเชื่อว่าใช้พื้นที่ชี้แจงแทนขนาดตัวอักษรและอื่น ๆ เหมาะสำหรับลำดับโปรตีนและยีนเท่านั้น)

วิธีการเขียนโปรแกรมแบบไดนามิกที่ใช้มีแนวโน้มที่จะเป็น O (mn) ในกรณีที่เลวร้ายที่สุดซึ่งจะช้าเกินไป ฉันเชื่อว่ามีการแก้ไขอัลกอริทึม Boyer-Moore สำหรับเรื่องนี้ แต่ฉันไม่สามารถรับมือกับเอกสารเหล่านี้ได้ ฉันไม่มีการสมัครสมาชิกเพื่อเข้าถึงวารสารหรือสิ่งพิมพ์ทางวิชาการดังนั้นการอ้างอิงใด ๆ จะต้องเป็นสาธารณสมบัติ

ฉันขอขอบคุณพอยน์เตอร์หรือการอ้างอิงถึงเอกสารที่พร้อมใช้งานฟรีหรืออัลกอริทึมสำหรับปัญหานี้


2
หากรูปแบบได้รับการแก้ไข (แต่ข้อความที่จะจับคู่แตกต่างกันไป) คุณอาจสร้างหุ่นยนต์ที่มีขอบเขต จำกัด และรันข้อความผ่านสิ่งนั้น นอกจากนี้ยังมีอัลกอริทึมที่ใช้แผนผังต่อท้าย (โดยปกติจะดีถ้าข้อความมีค่าคงที่และรูปแบบแตกต่างกันไป แต่ยังใช้ได้หากทั้งสองแตกต่างกัน) คุณอาจพบการอ้างอิงบางอย่างบนเว็บ (ยังไม่ได้เพิ่มคำตอบเนื่องจากฉันไม่แน่ใจเกี่ยวกับอัลกอริธึมที่ใช้แผนภูมิต่อท้ายหากบางคนรู้โปรดอย่าลังเลที่จะแสดงความคิดเห็นนี้)
Aryabhata

@Aryabhata ขอบคุณ! ทั้งรูปแบบและการเปลี่ยนแปลงข้อความ ในบริบทนั้นการสร้างหุ่นยนต์ จำกัด จะมีราคาแพงเกินไปโดยเฉพาะอย่างยิ่งเมื่อรวมขอบเขตสำหรับ 1 การไม่ตรงกัน สำหรับส่วนต่อท้ายต้นไม้ / ส่วนต่อท้ายฉันไม่เคยใช้พวกเขาและรู้เพียงเล็กน้อยเกี่ยวกับพวกเขา แต่อยู่ภายใต้การแสดงผลที่พวกเขาสร้างและมีประสิทธิภาพช้าสำหรับการจับคู่ที่แน่นอน แต่ฉันจะสำรวจตัวเลือกนี้เพิ่มเติม ตัวชี้ใด ๆ ในทิศทางนี้หรือในทิศทางอื่นใดจะมีประโยชน์มากที่สุด!
Paresh

1
ไม่สามารถใช้ต้นไม้ต่อท้ายสำหรับการแข่งขันโดยประมาณได้เช่นกัน อย่างน้อยวิกิอ้างว่าดังนั้น: en.wikipedia.org/wiki/Suffix_tree
Aryabhata

คำตอบ:


5

ส่วนต่อท้ายอาร์เรย์สามารถใช้สำหรับปัญหานี้ พวกมันมีตำแหน่งเริ่มต้นของแต่ละคำต่อท้ายของสตริงที่เรียงลำดับตามคำศัพท์ แม้ว่าพวกเขาสามารถสร้างอย่างไร้เดียงสาในความซับซ้อนมีวิธีการสร้างพวกเขาในความซับซ้อนΘ ( n ) ดูตัวอย่างนี้และนี้ ให้เราเรียก SA ต่อท้ายอาร์เรย์นี้O(nเข้าสู่ระบบn)Θ(n)

เมื่อสร้างส่วนต่อท้ายแล้วเราจำเป็นต้องสร้างอาร์เรย์ Longfix Common Prefix (LCP) สำหรับอาร์เรย์ต่อท้าย อาร์เรย์ LCP เก็บความยาวของคำนำหน้าทั่วไปที่ยาวที่สุดระหว่างคำนำหน้าสองคำติดต่อกันในอาร์เรย์คำต่อท้าย ดังนั้น LCP [i] มีความยาวของคำนำหน้าทั่วไปที่ยาวที่สุดระหว่าง SA [i] และ SA [i + 1] อาร์เรย์นี้ยังสามารถสร้างขึ้นในเส้นเวลา: ดูที่นี่ , ที่นี่และที่นี่สำหรับการอ้างอิงที่ดีบางอย่าง

ตอนนี้ในการคำนวณความยาวของคำนำหน้าที่ยาวที่สุดที่ใช้ร่วมกันกับสองคำต่อท้ายใด ๆในแผนภูมิต่อท้าย (แทนคำต่อท้ายที่ต่อเนื่องกัน) เราจำเป็นต้องใช้โครงสร้างข้อมูลRMQบางส่วน มันได้รับการแสดงในการอ้างอิงข้างต้น (และสามารถมองเห็นได้ง่ายถ้าอาร์เรย์ถูกมองเห็นเป็นต้นไม้ต่อท้าย) ว่าความยาวของคำนำหน้าทั่วไปที่ยาวที่สุดระหว่างสองคำต่อท้ายมีตำแหน่งและv ( u < v ) ในอาร์เรย์ต่อท้าย สามารถรับได้เป็นm i n u < = k < = v - 1 L C P [ k ]ยูโวลต์ยู<โวลต์minu<=k<=v1LCP[k]. RMQ ดีสามารถ pre-กระบวนการอาร์เรย์O ( n )หรือO ( n log n )เวลาและตอบสนองต่อคำสั่งของรูปแบบL C P [ U , V ]ในO ( 1 )เวลา ดูที่นี่สำหรับอัลกอริทึม RMQ ที่ประสบความสำเร็จและที่นี่สำหรับการสอนที่ดีเกี่ยวกับ RMQ และความสัมพันธ์ (และการลดลง) ระหว่าง LCA และ RMQ นี่เป็นอีกวิธีทางเลือกที่ดีLCPO(n)O(nlogn)LCP[u,v]O(1)

ด้วยข้อมูลนี้เราได้สร้างอาร์เรย์คำต่อท้ายและอาร์เรย์ที่เกี่ยวข้อง (ตามที่อธิบายไว้ข้างต้น) สำหรับการต่อข้อมูลของสองสตริงด้วยตัวคั่นระหว่าง (เช่น T # P โดยที่ '#' ไม่ได้เกิดขึ้นในทั้งสองสตริง) จากนั้นเราสามารถทำการจับคู่สตริง k ที่ไม่ตรงกันโดยใช้วิธี "จิงโจ้" สิ่งนี้และสิ่งนี้อธิบายวิธีการจิงโจ้ในบริบทของต้นไม้ต่อท้าย แต่สามารถนำไปใช้โดยตรงกับอาร์เรย์ต่อท้ายด้วย สำหรับดัชนีทุกของข้อความTหาL C Pของคำต่อท้ายของTเริ่มต้นที่ฉันและคำต่อท้ายของPiTLCPTiPเริ่มต้นที่ 0 นี้จะช่วยให้สถานที่ตั้งหลังจากที่ไม่ตรงกันครั้งแรกเกิดขึ้นเมื่อจับคู่กับT [ ผม ] ให้ระยะเวลานี้เป็นลิตร 0 ข้ามอักขระที่ไม่ตรงกันในTและPแล้วลองจับคู่สตริงที่เหลือ นั่นคือหาL C PของT [ i + l 0 + 1 ]และP [ l 0 + 1 ]อีกครั้ง ทำซ้ำจนกว่าคุณจะได้รับkไม่ตรงกันหรือสตริงเสร็จสิ้น แต่ละPT[i]l0TPLCPT[i+l0+1]P[l0+1]kคือ O ( 1 ) มี O ( k ) L ซีพี 's สำหรับดัชนีแต่ละฉันของ T , ให้นี้ซับซ้อนรวมของ O ( n k )LCPO(1)O(k) LCPiTO(nk)

ฉันใช้วิธีที่ง่ายกว่าในการใช้ RMQ ที่ให้ความซับซ้อนโดยรวมของหรือO ( n k + n บันทึกn )ถ้าm = O ( n )แต่มัน สามารถทำได้ในO ( n k )เช่นกันตามที่อธิบายไว้ข้างต้น อาจมีวิธีการอื่น ๆ โดยตรงสำหรับปัญหานี้ แต่นี่เป็นวิธีที่มีประสิทธิภาพและทั่วไปที่สามารถนำไปใช้กับปัญหาที่คล้ายกันมากมายO(nk+(n+m)log(n+m))O(nk+nlogn)m=O(n)O(nk)


ที่ดี! ฉันได้อ่านรายการสิ่งที่ต้องทำของฉันตอนนี้ :-)
Aryabhata

ลิงค์ siam.org ในย่อหน้าที่สองเสีย แต่สามารถดูลิงค์ของกระดาษได้ที่นี่epubs.siam.org/doi/pdf/10.1137/1.9781611972917.3
leecbaker

4

ด้านล่างนี้เป็นอัลกอริทึมคาดหวัง(ซึ่งสามารถขยายไปยังkอื่นได้ทำให้เป็นO ( n k + m ) ) (ฉันยังไม่ได้ทำการคำนวณเพื่อพิสูจน์ว่าเป็นเช่นนั้น)O(n+m)kO(nk+m)

แนวคิดนี้คล้ายกับอัลกอริธึมการแฮชของRabin-Karpสำหรับการจับคู่สตริงย่อยที่แน่นอน

แนวคิดคือการแยกแต่ละสตริงของความยาวออกเป็น2 kบล็อกของm / 2 kแต่ละขนาดและคำนวณแฮชการกลิ้งสำหรับแต่ละบล็อก (ให้ค่าแฮ2 k ) และเปรียบเทียบค่าแฮช2 kเหล่านั้นกับรูปแบบหนึ่งm2km/2k2k2k

เราอนุญาตค่าไม่ตรงกันในค่าเหล่านั้นมากที่สุดk

หากเกิดข้อผิดพลาดมากกว่าเราจะปฏิเสธและดำเนินการต่อ มิฉะนั้นเราจะพยายามยืนยันการจับคู่โดยประมาณk

ฉันคาดหวัง (ข้อแม้: ยังไม่ได้ลองด้วยตัวเอง) สิ่งนี้อาจจะเร็วกว่าในทางปฏิบัติและอาจจะง่ายกว่าในการเขียนโค้ด / บำรุงรักษามากกว่าการใช้วิธีแบบต่อท้ายทรี


แค่ต้องการความกระจ่าง โดย ".. แยกสตริงที่มีความยาวแต่ละ m ออกเป็น 2k บล็อกที่มีขนาด m / 2k แต่ละรายการ ... " คุณหมายถึงการแยกแต่ละสตริงย่อยของความยาว m ใน T (ของความยาว n) ออกเป็น 2k บล็อก และแฮชนี้สามารถคำนวณได้ใน O (n) โดยวิธีแฮชกลิ้ง จากนั้นสตริงรูปแบบจะถูกแบ่งออกเป็น 2k บล็อกและจะเปรียบเทียบค่าแฮชที่สอดคล้องกันซึ่งจะทำให้ค่าเผื่อสำหรับบล็อก k สูงสุดไม่ตรงกัน ถ้าเป็นเช่นนั้นเราจะสามารถทิ้งทุกกรณีที่มีจำนวนการจับคู่ที่ไม่ตรงกันมากกว่า k ฉันเข้าใจถูกมั้ย
Paresh

kΩ(nk)O(n)

ฉันชอบวิธีนี้! ถึงกระนั้นวิธีการนี้มีความรวดเร็วโดยทั่วไป แต่ลดระดับลงเป็น O (mnk) หากจำนวนการแข่งขันสูง (O (n) ตรงกัน) เมื่อคำนึงถึงสิ่งนี้ฉันยังคงแฮชสองม้วนภายใต้สมมติฐานที่ว่าทั้งสองไม่สามารถมีการชนกันของอินพุตเดียวกัน (ฉันไม่ได้ทำสิ่งนี้ทางคณิตศาสตร์เพราะฉันต้องการเห็นความเร็ว) ด้วยวิธีนี้เราไม่จำเป็นต้องตรวจสอบการแข่งขันแบบ char-by-char หากทั้งสองแฮชเห็นด้วย โดยทั่วไปค่อนข้างเร็ว แต่ก็ช้าถ้าจำนวนการแข่งขันมีขนาดใหญ่ ด้วยวิธีนี้และตามที่คุณแนะนำมันช้าสำหรับการแข่งขันนัดใหญ่
Paresh

ม.ม./2kม.O(nkม.)

ม.ม./2k2kk+1k+Ω(nม.)mm/2k
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.