ตกลงดังนั้นฉันไม่ฟังเหมือนคนงี่เง่าฉันจะระบุปัญหา / ข้อกำหนดเพิ่มเติมอย่างชัดเจน:
- Needle (pattern) และ haystack (text to search) เป็นทั้งสตริง C-style ที่สิ้นสุดด้วยค่า null ไม่มีข้อมูลความยาวให้; หากจำเป็นจะต้องคำนวณ
- ฟังก์ชั่นควรกลับตัวชี้ไปที่การแข่งขันครั้งแรกหรือ
NULL
หากไม่พบการแข่งขัน - ไม่อนุญาตให้ใช้กรณีที่ล้มเหลว ซึ่งหมายความว่าอัลกอริทึมใด ๆ ที่มีความต้องการพื้นที่เก็บข้อมูลไม่คงที่ (หรือขนาดใหญ่คงที่) จะต้องมีกรณีสำรองสำหรับความล้มเหลวในการจัดสรร
- การใช้งานจะต้องอยู่ใน C แม้ว่าคำอธิบายที่ดีของอัลกอริทึม
... รวมถึงสิ่งที่ฉันหมายถึงโดย "เร็วที่สุด":
- กำหนด
O(n)
ที่n
= ความยาวกองหญ้า (แต่อาจเป็นไปได้ที่จะใช้แนวคิดจากอัลกอริทึมซึ่งโดยปกติO(nm)
(ตัวอย่างเช่นแฮชการกลิ้ง) หากรวมกับอัลกอริธึมที่แข็งแกร่งกว่าเพื่อให้ได้O(n)
ผลลัพธ์ที่กำหนดขึ้นมา - ไม่เคยทำ (วัดได้; นาฬิกาสองสามนาฬิกา
if (!needle[1])
ฯลฯ ไม่เป็นไร) แย่กว่าอัลกอริธึมกำลังเดรัจฉานไร้เดียงสาโดยเฉพาะอย่างยิ่งในเข็มสั้น ๆ ซึ่งน่าจะเป็นกรณีที่พบบ่อยที่สุด (ค่าโสหุ้ยการประมวลผลล่วงหน้าที่ไม่มีเงื่อนไขอย่างหนักนั้นไม่ดีเช่นเดียวกับที่พยายามปรับปรุงค่าสัมประสิทธิ์เชิงเส้นสำหรับเข็มทางพยาธิวิทยาด้วยค่าใช้จ่ายของเข็มที่มีแนวโน้ม) - ได้รับเข็มและกองหญ้าโดยพลการประสิทธิภาพเทียบเคียงหรือดีกว่า (ไม่เลวร้ายยิ่งกว่าการค้นหานานกว่า 50%) เทียบกับอัลกอริทึมที่ใช้งานกันอย่างแพร่หลายอื่น ๆ
- นอกเหนือจากเงื่อนไขเหล่านี้ฉันยังคงนิยามคำว่า "เร็วที่สุด" ที่เปิดกว้าง คำตอบที่ดีควรอธิบายว่าทำไมคุณจึงพิจารณาวิธีที่คุณแนะนำว่า "เร็วที่สุด"
การใช้งานปัจจุบันของฉันทำงานช้าลงประมาณ 10% และเร็วขึ้น 8 เท่า (ขึ้นอยู่กับอินพุต) กว่าการใช้ Two-Way ของ glibc
อัปเดต: อัลกอริทึมที่เหมาะสมที่สุดของฉันในปัจจุบันเป็นดังนี้:
- สำหรับเข็มมีความยาว 1
strchr
ใช้ - สำหรับเข็มที่มีความยาว 2-4 ให้ใช้คำของเครื่องเพื่อเปรียบเทียบ 2-4 ไบต์พร้อมกันดังนี้: โหลดเข็มในจำนวนเต็ม 16- หรือ 32- บิตพร้อมกับเปลี่ยนบิตและวงจรไบต์เก่าออก / ใหม่ไบต์จากกองหญ้าในแต่ละการทำซ้ำ . ทุกไบต์ของกองหญ้าจะถูกอ่านอย่างแน่นอนหนึ่งครั้งและเกิดการตรวจสอบกับ 0 (จุดสิ้นสุดของสตริง) และการเปรียบเทียบแบบ 16 หรือ 32 บิตหนึ่งรายการ
- สำหรับเข็มที่มีความยาว> 4 ให้ใช้อัลกอริทึมแบบสองทางพร้อมกับตารางการเลื่อนที่ไม่ดี (เช่น Boyer-Moore) ซึ่งใช้กับไบต์สุดท้ายของหน้าต่างเท่านั้น เพื่อหลีกเลี่ยงค่าใช้จ่ายในการเริ่มต้นตาราง 1kb ซึ่งจะเป็นผลขาดทุนสุทธิสำหรับเข็มยาวปานกลางจำนวนมากฉันเก็บบิตอาร์เรย์ (32 ไบต์) ที่ทำเครื่องหมายว่ารายการใดในตารางเลื่อนถูกเตรียมใช้งาน บิตที่ไม่ได้ตั้งค่านั้นสอดคล้องกับค่าไบต์ที่ไม่เคยปรากฏในเข็มซึ่งเป็นไปได้ที่การเปลี่ยนความยาวเข็มเต็มเป็นไปได้
คำถามใหญ่ที่เหลืออยู่ในใจของฉันคือ:
- มีวิธีการใช้ตารางกะที่ดีกว่านี้หรือไม่? Boyer-Moore ใช้งานได้อย่างดีที่สุดโดยการสแกนไปทางด้านหลัง (ขวาไปซ้าย) แต่ Two-Way ต้องการการสแกนจากซ้ายไปขวา
- เพียงสองขั้นตอนวิธีการทำงานของผู้สมัครที่ฉันได้พบสำหรับกรณีทั่วไป (ไม่ออกจากหน่วยความจำหรือประสิทธิภาพการทำงานของกำลังสองเงื่อนไข) เป็นTwo-Wayและการจับคู่สายในตัวอักษรที่ได้รับคำสั่ง แต่มีกรณีที่ตรวจพบได้ง่ายซึ่งอัลกอริธึมที่แตกต่างกันเหมาะสมหรือไม่ แน่นอนว่าหลาย ๆ
O(m)
(ที่m
ความยาวของเข็ม) ในอัลกอริทึมของอวกาศสามารถนำมาใช้เพื่อการm<100
นั้นได้ นอกจากนี้ยังเป็นไปได้ที่จะใช้อัลกอริธึมที่เป็นกำลังสองกรณีที่เลวร้ายที่สุดถ้ามีการทดสอบง่าย ๆ สำหรับเข็มที่ต้องการเวลาเชิงเส้นเท่านั้น
คะแนนโบนัสสำหรับ:
- คุณสามารถปรับปรุงประสิทธิภาพโดยการสมมติว่าเข็มและกองหญ้านั้นเป็น UTF-8 ที่มีรูปแบบที่ดีหรือไม่? (ด้วยอักขระที่มีความยาวไบต์ต่างกันรูปแบบที่ดีจะกำหนดข้อกำหนดการจัดเรียงสตริงบางส่วนระหว่างเข็มและกองหญ้าและอนุญาตให้มีการเปลี่ยน 2-4 ไบต์อัตโนมัติเมื่อพบหัวไบต์ที่ไม่ตรงกัน แต่ข้อ จำกัด เหล่านี้ทำให้คุณซื้ออะไรมากไป การคำนวณคำต่อท้ายสูงสุดการเปลี่ยนแปลงคำต่อท้ายที่ดี ฯลฯ ให้อัลกอริธึมต่างๆแล้วหรือยัง?)
หมายเหตุ:ฉันตระหนักดีถึงอัลกอริธึมส่วนใหญ่ที่นั่นไม่ใช่ว่าพวกเขาปฏิบัติได้ดีเพียงใด ต่อไปนี้เป็นข้อมูลอ้างอิงที่ดีเพื่อให้ผู้คนไม่ให้การอ้างอิงฉันเกี่ยวกับอัลกอริทึมเป็นความคิดเห็น / คำตอบ: http://www-igm.univ-mlv.fr/~lecroq/string/index.html
strstr
ในภายหลังเป็นอะไรบางอย่างในภายหลังดังนั้นฉันจึงไม่ได้อ่านบทความที่คุณเชื่อมโยงอย่างถูกต้อง แต่มันฟังดูดีมาก ขอขอบคุณและขอโทษที่ไม่ติดต่อคุณ