ฉันจะตรวจสอบว่าสตริงที่ทำจากสตริงย่อยเดียวกันทั้งหมดได้อย่างไร


128

ฉันต้องสร้างฟังก์ชั่นที่รับสายและมันควรจะกลับมาtrueหรือไม่ก็falseขึ้นอยู่กับว่าอินพุตประกอบด้วยลำดับอักขระซ้ำแล้วซ้ำอีก ความยาวของสตริงที่กำหนดนั้นมีค่ามากกว่า1และลำดับอักขระต้องมีการทำซ้ำอย่างน้อยหนึ่งครั้ง

"aa" // true(entirely contains two strings "a")
"aaa" //true(entirely contains three string "a")
"abcabcabc" //true(entirely containas three strings "abc")

"aba" //false(At least there should be two same substrings and nothing more)
"ababa" //false("ab" exists twice but "a" is extra so false)

ฉันได้สร้างฟังก์ชั่นด้านล่าง:

function check(str){
  if(!(str.length && str.length - 1)) return false;
  let temp = '';
  for(let i = 0;i<=str.length/2;i++){
    temp += str[i]
    //console.log(str.replace(new RegExp(temp,"g"),''))
    if(!str.replace(new RegExp(temp,"g"),'')) return true;
  }
  return false;
}

console.log(check('aa')) //true
console.log(check('aaa')) //true
console.log(check('abcabcabc')) //true
console.log(check('aba')) //false
console.log(check('ababa')) //false

การตรวจสอบสิ่งนี้เป็นส่วนหนึ่งของปัญหาจริง ฉันไม่สามารถซื้อโซลูชันที่ไม่มีประสิทธิภาพเช่นนี้ได้ ก่อนอื่นมันวนไปครึ่งหนึ่งของสาย

ปัญหาที่สองคือการใช้replace()ในแต่ละวงซึ่งทำให้ช้า มีวิธีแก้ปัญหาที่ดีกว่าเกี่ยวกับประสิทธิภาพหรือไม่


19
ลิงค์นี้อาจมีประโยชน์กับคุณ ฉันมักจะพบว่า geekforgeeks เป็นแหล่งข้อมูลที่ดีสำหรับปัญหาอัลกอริทึม - geeksforgeeks.org/…
Leron_says_get_back_Monica

9
คุณสนใจไหมถ้าฉันยืมสิ่งนี้และทำให้มันเป็นความท้าทายในการเขียนโปรแกรมบนเว็บไซต์แลกเปลี่ยนกอล์ฟ
ouflak

7
@ อำพรางคุณสามารถทำได้
Maheer Ali

12
ในกรณีที่คุณอยากรู้อยากเห็นcodegolf.stackexchange.com/questions/184682/…
ouflak

24
@Shidersz การใช้โครงข่ายประสาทเทียมทำให้รู้สึกเหมือนใช้ปืนใหญ่ยิงยุง
JAD

คำตอบ:


186

มีทฤษฎีบทเล็ก ๆ เกี่ยวกับสตริงเช่นนี้

สตริงประกอบด้วยรูปแบบเดียวกันซ้ำหลายครั้งถ้าหากสตริงนั้นเป็นการหมุนแบบไม่ย่อยเอง

ที่นี่การหมุนหมายถึงการลบจำนวนอักขระจากด้านหน้าของสตริงและย้ายไปทางด้านหลัง ตัวอย่างเช่นสตริงhelloสามารถหมุนเพื่อสร้างสตริงใด ๆ เหล่านี้:

hello (the trivial rotation)
elloh 
llohe 
lohel 
ohell 

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

ตอนนี้คำถามคือวิธีการตรวจสอบว่าเป็นกรณีนี้ เพื่อให้มีทฤษฎีบทที่สวยงามอีกประการหนึ่งที่เราสามารถใช้:

ถ้า x และ y เป็นสตริงที่มีความยาวเท่ากันดังนั้น x คือการหมุนของ y ถ้าหาก x เป็นซับสตริงของ yy

ดังตัวอย่างเราจะเห็นว่าlohelการหมุนมีhelloดังนี้:

hellohello
   ^^^^^

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

function check(str) {
    return (str + str).indexOf(str, 1) !== str.length;
}

สมมติว่าindexOfมีการใช้งานโดยใช้อัลกอริทึมการจับคู่สตริงอย่างรวดเร็วซึ่งจะทำงานในเวลา O (n) โดยที่ n คือความยาวของสตริงการป้อน

หวังว่านี่จะช่วยได้!


13
ดีมาก! ฉันเพิ่มไปยังหน้ามาตรฐาน jsPerfแล้ว
user42723

10
@ user42723 ยอดเยี่ยม! ดูเหมือนว่ามันจะเร็วจริงๆ
templatetypedef

5
FYI: ฉันมีเวลายากที่จะเชื่อประโยคนั้นจนกว่าฉันจะย้อนกลับถ้อยคำ: "สตริงเป็นการหมุนที่ไม่ซ้ำซากของตัวเองถ้าหากมันประกอบด้วยรูปแบบเดียวกันซ้ำหลายครั้ง" ไปคิด
Axel Podehl

11
คุณมีการอ้างอิงถึงทฤษฎีบทเหล่านั้นหรือไม่?
HRK44

4
ผมคิดว่าคำสั่งแรกเป็นเช่นเดียวกับ " แทรก 2.3 : ถ้า x และการหมุนของ x มีค่าเท่ากันแล้ว x มีการทำซ้ำ" ที่doi.org/10.1016/j.tcs.2008.04.020 ดูเพิ่มเติมได้ที่: stackoverflow.com/a/2553533/1462295
BurnsBA

67

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

function check(str) {
  return /^(.+)\1+$/.test(str)
}

console.log(check('aa')) //true
console.log(check('aaa')) //true
console.log(check('abcabcabc')) //true
console.log(check('aba')) //false
console.log(check('ababa')) //false

ใน RegExp ด้านบน:

  1. ^และ$ย่อมาจากจุดเริ่มต้นและจุดสิ้นสุดจุดยึดเพื่อทำนายตำแหน่ง
  2. (.+)จับรูปแบบใด ๆ และจับค่า (ยกเว้น\n)
  3. \1คือการอ้างอิงย้อนกลับของค่าที่ดัก\1+จับไว้ก่อน

คำอธิบาย Regex ที่นี่

สำหรับการดีบัก RegExp ให้ใช้: https://regex101.com/r/pqlAuP/1/debugger

ประสิทธิภาพการทำงาน: https://jsperf.com/reegx-and-loop/13


2
คุณสามารถอธิบายสิ่งที่เราบรรทัดนี้จะทำผลตอบแทน /^(.+)\1+$/.test(str)
Thanveer ชาห์

34
ความซับซ้อนของโซลูชันนี้คืออะไร ฉันไม่แน่ใจอย่างแน่นอน แต่ดูเหมือนจะไม่เร็วกว่าที่ OP ใช้มาก
Leron_says_get_back_Monica

8
@PranavCBalan ฉันไม่เก่งเรื่องอัลกอริธึมนั่นเป็นเหตุผลที่ฉันเขียนในส่วนความเห็น อย่างไรก็ตามฉันมีหลายสิ่งที่ต้องพูดถึง - OP มีวิธีแก้ปัญหาที่ใช้งานได้อยู่แล้วดังนั้นเขาจึงถามหาสิ่งที่จะทำให้เขามีประสิทธิภาพที่ดีขึ้นและคุณยังไม่ได้อธิบายว่าโซลูชันของคุณมีประสิทธิภาพสูงกว่าอย่างไร ตัวย่อไม่ได้หมายความว่าเร็วขึ้น นอกจากนี้จากลิงก์ที่คุณให้: If you use normal (TCS:no backreference, concatenation,alternation,Kleene star) regexp and regexp is already compiled then it's O(n).แต่ในขณะที่คุณเขียนว่าคุณกำลังใช้การอ้างอิงย้อนกลับดังนั้นยังคงเป็น O (n) หรือไม่
Leron_says_get_back_Monica

5
คุณสามารถใช้[\s\S]แทน.หากคุณต้องการจับคู่อักขระขึ้นบรรทัดใหม่ในลักษณะเดียวกับอักขระอื่น อักขระจุดไม่ตรงกับบรรทัดใหม่ การค้นหาทางเลือกสำหรับอักขระ white-space และ non-whitespace ทั้งหมดซึ่งหมายความว่าการขึ้นบรรทัดใหม่จะรวมอยู่ในการจับคู่ (โปรดทราบว่านี่จะเร็วกว่าการใช้งานง่าย(.|[\r\n])กว่า) อย่างไรก็ตามหากสตริงไม่มีการขึ้นบรรทัดใหม่แน่นอนจะเป็นการง่าย.ที่สุด หมายเหตุสิ่งนี้จะง่ายกว่านี้มากหากมีการใช้งานค่าสถานะ dotall
HappyDog

2
ไม่ได้/^(.+?)\1+$/เล็ก ๆ น้อย ๆ ได้เร็วขึ้น? (12 ขั้นตอนเทียบกับ 20 ขั้นตอน)
ออนไลน์ Thomas

29

บางทีวิธีอัลกอริทึมที่เร็วที่สุดคือการสร้างฟังก์ชัน Zในเวลาเชิงเส้น:

ฟังก์ชัน Z สำหรับสตริงนี้คืออาร์เรย์ที่มีความยาว n โดยที่องค์ประกอบ i-th นั้นมีค่าเท่ากับจำนวนอักขระที่มากที่สุดโดยเริ่มจากตำแหน่ง i ที่ตรงกับอักขระตัวแรกของ s

กล่าวอีกนัยหนึ่ง z [i] คือความยาวของคำนำหน้าทั่วไปที่ยาวที่สุดระหว่าง s และคำต่อท้ายของ s เริ่มต้นที่ i

การใช้ C ++ สำหรับการอ้างอิง:

vector<int> z_function(string s) {
    int n = (int) s.length();
    vector<int> z(n);
    for (int i = 1, l = 0, r = 0; i < n; ++i) {
        if (i <= r)
            z[i] = min (r - i + 1, z[i - l]);
        while (i + z[i] < n && s[z[i]] == s[i + z[i]])
            ++z[i];
        if (i + z[i] - 1 > r)
            l = i, r = i + z[i] - 1;
    }
    return z;
}

การใช้งาน JavaScript
เพิ่มการเพิ่มประสิทธิภาพ - สร้างครึ่งหนึ่งของอาเรย์ Z และออกก่อน

function z_function(s) {
  var n = s.length;
  var z = Array(n).fill(0);
  var i, l, r;
  //for our task we need only a half of z-array
  for (i = 1, l = 0, r = 0; i <= n/2; ++i) {
    if (i <= r)
      z[i] = Math.min(r - i + 1, z[i - l]);
    while (i + z[i] < n && s[z[i]] == s[i + z[i]])
      ++z[i];

      //we can check condition and return here
     if (z[i] + i === n && n % i === 0) return true;
    
    if (i + z[i] - 1 > r)
      l = i, r = i + z[i] - 1;
  }
  return false; 
  //return z.some((zi, i) => (i + zi) === n && n % i === 0);
}
console.log(z_function("abacabacabac"));
console.log(z_function("abcab"));

จากนั้นคุณต้องตรวจสอบดัชนีiที่แบ่ง n หากคุณพบดังกล่าวiที่i+z[i]=nแล้วสตริงsจะสามารถบีบอัดความยาวและคุณสามารถกลับitrue

ตัวอย่างเช่นสำหรับ

string s= 'abacabacabac'  with length n=12`

ซีเรย์คือ

(0, 0, 1, 0, 8, 0, 1, 0, 4, 0, 1, 0)

และเราสามารถหาสิ่งนั้นได้

i=4
i+z[i] = 4 + 8 = 12 = n
and
n % i = 12 % 4 = 0`

ดังนั้นsอาจแสดงเป็นสตริงย่อยของความยาว 4 ซ้ำสามครั้ง


3
return z.some((zi, i) => (i + zi) === n && n % i === 0)
Pranav C Balan

2
ขอขอบคุณที่เพิ่มสิ่ง JavaScript ให้กับ Salman A และ Pranav C Balan
MBo

1
แนวทางอื่นโดยหลีกเลี่ยงการวนซ้ำเพิ่มเติมconst check = (s) => { let n = s.length; let z = Array(n).fill(0); for (let i = 1, l = 0, r = 0; i < n; ++i) { if (i <= r) z[i] = Math.min(r - i + 1, z[i - l]); while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i]; // check condition here and return if (z[i] + i === n && n % i === 0) return true; if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1; } // or return false return false; }
Pranav C Balan

2
การใช้ฟังก์ชั่น z เป็นความคิดที่ดี แต่มันคือ 'data -heavy' มันมีข้อมูลจำนวนมากที่ไม่เคยใช้
Axel Podehl

@Axel Podehl อย่างไรก็ตามมันถือว่าสตริงในเวลา O (n) (แต่ละถ่านถูกใช้อย่างมากที่สุดสองครั้ง) ไม่ว่าในกรณีใดเราต้องตรวจสอบถ่านทุกตัวดังนั้นจึงไม่มีอัลกอริทึมที่เร็วกว่าในทางทฤษฎี นอกจากนี้ในการแก้ไขล่าสุดฉัน จำกัด การคำนวณโดย 1/2 ของความยาวสตริง
MBo

23

ฉันอ่านคำตอบของ gnasher729 และนำไปใช้ ความคิดคือถ้ามีการทำซ้ำใด ๆ แล้วจะต้องมีจำนวนซ้ำของ (เช่น)

function* primeFactors (n) {
    for (var k = 2; k*k <= n; k++) {
        if (n % k == 0) {
            yield k
            do {n /= k} while (n % k == 0)
        }
    }
    if (n > 1) yield n
}

function check (str) {
    var n = str.length
    primeloop:
    for (var p of primeFactors(n)) {
        var l = n/p
        var s = str.substring(0, l)
        for (var j=1; j<p; j++) {
            if (s != str.substring(l*j, l*(j+1))) continue primeloop
        }
        return true
    }
    return false
}

อัลกอริทึมที่แตกต่างกันเล็กน้อยคือ:

function check (str) {
    var n = str.length
    for (var p of primeFactors(n)) {
        var l = n/p
        if (str.substring(0, n-l) == str.substring(l)) return true
    }
    return false
}

ฉันได้อัปเดตหน้า jsPerfที่มีอัลกอริทึมที่ใช้ในหน้านี้แล้ว


ดูเหมือนว่าจะเร็วมากเนื่องจากจะข้ามการตรวจสอบที่ไม่จำเป็น
Pranav C Balan

1
ดีมากฉันแค่คิดว่าฉันจะตรวจสอบว่าตัวอักษรตัวแรก reoccurs ในตำแหน่งที่ระบุก่อนที่จะทำการเรียกสายย่อย
Ben Voigt

สำหรับคนที่สะดุดfunction*เป็นครั้งแรกเช่นฉันมันเป็นการประกาศกำเนิดไม่ใช่หน้าที่ปกติ ดูMDN
Julien Rousé

17

สมมติว่าสตริง S มีความยาว N และทำซ้ำของสตริงย่อย s ดังนั้นความยาวของ s จะหาร N ตัวอย่างเช่นถ้า S มีความยาว 15 สตริงย่อยนั้นจะมีความยาว 1, 3 หรือ 5

ปล่อยให้ S ทำจาก (p * q) สำเนาของ s จากนั้น S ก็ทำจาก p สำเนาของ (s, ซ้ำ q ครั้ง) ดังนั้นเราจึงมีสองกรณี: ถ้า N เป็นไพรม์หรือ 1 ดังนั้น S สามารถสร้างได้เพียงสำเนาของซับสตริงที่มีความยาว 1 เท่านั้นหาก N เป็นคอมโพสิตเราจะต้องตรวจสอบสตริงย่อยของความยาว N / p เพื่อแบ่งส่วน p ความยาวของ S.

ดังนั้นหา N = ความยาวของ S จากนั้นหาปัจจัยสำคัญทั้งหมดในเวลา O (sqrt (N)) หากมีเพียงปัจจัยเดียว N ให้ตรวจสอบว่า S เป็นสตริงเดียวกันซ้ำ N ครั้งมิฉะนั้นสำหรับแต่ละปัจจัยหลัก p ตรวจสอบว่า S ประกอบด้วยการยกเลิก p ของอักขระ N / p แรก


ฉันไม่ได้ตรวจสอบวิธีแก้ไขปัญหาอื่น ๆ แต่ดูเหมือนจะรวดเร็วมาก คุณสามารถปล่อยส่วน "ถ้ามีเพียงปัจจัยเดียว N ให้ตรวจสอบ ... หรืออย่างอื่น" เพื่อความเรียบง่ายเนื่องจากนี่ไม่ใช่กรณีพิเศษ ยินดีที่ได้เห็นการใช้งานจาวาสคริปต์ที่สามารถเรียกใช้ใน jsPerf ถัดจากการใช้งานอื่น ๆ
user42723

1
ฉันได้นำสิ่งนี้ไปใช้ในคำตอบของฉันแล้ว
user42723

10

ฉันคิดว่าฟังก์ชั่นวนซ้ำอาจเร็วมากเช่นกัน การสังเกตครั้งแรกคือความยาวของรูปแบบการทำซ้ำสูงสุดคือครึ่งหนึ่งเท่ากับสตริงทั้งหมด และเราสามารถทดสอบความยาวของรูปแบบการทำซ้ำที่เป็นไปได้ทั้งหมด: 1, 2, 3, ... , str.length / 2

ฟังก์ชันเรียกซ้ำ isRepeat (p, str) ทดสอบว่ารูปแบบนี้ซ้ำใน str

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

หากรูปแบบและ str ที่ทดสอบมีขนาดเท่ากันการเรียกซ้ำจะสิ้นสุดที่นี่ให้สำเร็จ

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

function check(str)
{
  if( str.length==1 ) return true; // trivial case
  for( var i=1;i<=str.length/2;i++ ) { // biggest possible repeated pattern has length/2 characters

    if( str.length%i!=0 ) continue; // pattern of size i doesn't fit
    
    var p = str.substring(0, i);
    if( isRepeating(p,str) ) return true;
  }
  return false;
}


function isRepeating(p, str)
{
  if( str.length>p.length ) { // maybe more than 2 occurences

    var left = str.substring(0,p.length);
    var right = str.substring(p.length, str.length);
    return left===p && isRepeating(p,right);
  }
  return str===p; 
}

console.log(check('aa')) //true
console.log(check('aaa')) //true 
console.log(check('abcabcabc')) //true
console.log(check('aba')) //false
console.log(check('ababa')) //false

ประสิทธิภาพการทำงาน: https://jsperf.com/reegx-and-loop/13


1
จะเร็วกว่าการตรวจสอบif( str===p.repeat(str.length/i) ) return true;แทนที่จะใช้ฟังก์ชันเรียกซ้ำหรือไม่
Chronocidal

1
ไม่ใส่ console.logs ในการทดสอบ jsperf เตรียมความพร้อมฟังก์ชั่นภายในส่วน Globals ยังเตรียมสายการทดสอบในส่วน Globals (ขออภัยไม่สามารถแก้ไข jsperf) ที่
ซัล

@ Salman - จุดดี ฉันเพิ่งแก้ไข jsperf จากบรรพบุรุษของฉัน (Pranav C) ครั้งแรกที่ฉันใช้ jsperf เครื่องมือเจ๋ง ๆ
Axel Podehl

@SalmanA: อัปเดต: jsperf.com/regex-and-loop/1 ... ขอบคุณสำหรับข้อมูล ... แม้ฉันไม่คุ้นเคยกับมัน (Jsperf) ... ขอบคุณสำหรับข้อมูล
Pranav C Balan

สวัสดีซัลมานขอบคุณมากสำหรับ jsperf.com/reegx-and-loop/10 - ใช่แล้วการทดสอบ perf แบบใหม่นั้นสมเหตุสมผลมากกว่า การตั้งค่าฟังก์ชั่นควรเป็นรหัสการเตรียมการ
Axel Podehl

7

เขียนสิ่งนี้ใน Python ฉันรู้ว่ามันไม่ใช่แพลตฟอร์ม แต่ใช้เวลา 30 นาที PS => PYTHON

def checkString(string):
    gap = 1 
    index= 0
    while index < len(string)/2:
        value  = [string[i:i+gap] for i in range(0,len(string),gap) ]

        x = [string[:gap]==eachVal for eachVal in value]

        if all(x):
            print("THEY ARE  EQUAL")
            break 

        gap = gap+1
        index= index+1 

checkString("aaeaaeaaeaae")

6

วิธีการของฉันคล้ายกับ gnasher729 ซึ่งมันใช้ความยาวที่เป็นไปได้ของซับสตริงเป็นจุดสนใจหลัก แต่มันก็น้อยกว่า math-y และกระบวนการที่เข้มข้น:

L: ความยาวของสตริงดั้งเดิม

S: ความยาวที่เป็นไปได้ของสตริงย่อยที่ถูกต้อง

วนซ้ำ S จาก (ส่วนจำนวนเต็มของ) L / 2 ถึง 1 ถ้า L / S เป็นจำนวนเต็มตรวจสอบสตริงเดิมของคุณกับตัวอักษร S กำปั้นของสตริงเดิมซ้ำ L / S ซ้ำ

เหตุผลในการวนซ้ำจาก L / 2 ไปข้างหลังและไม่ใช่จาก 1 เป็นต้นไปคือการได้สตริงย่อยที่ใหญ่ที่สุด ถ้าคุณต้องการ substring loop ที่เล็กที่สุดที่เป็นไปได้ตั้งแต่ 1 ถึง L / 2 ตัวอย่าง: "abababab" มีทั้ง "ab" และ "abab" เป็นสตริงย่อยที่เป็นไปได้ ซึ่งทั้งสองจะเร็วขึ้นหากคุณสนใจเฉพาะผลลัพธ์ที่แท้จริง / เท็จขึ้นอยู่กับประเภทของสตริง / สตริงย่อยที่จะนำไปใช้


5

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

IsRepeatedQ[list_] := Module[{n = Length@list},
   Round@N@Sum[list[[i]] Exp[2 Pi I i/n], {i, n}] == 0
];

รหัสนี้จะมองหาการสนับสนุน "เต็มความยาว" ซึ่งจะต้องเป็นศูนย์ในสตริงที่ซ้ำกัน แต่สตริงaccbbdจะถือว่ายังทำซ้ำเพราะมันเป็นผลรวมของสองสายซ้ำabababและ012012และ

ความคิดคือการใช้การแปลงฟูริเยร์อย่างรวดเร็วและมองหาสเปกตรัมความถี่ เมื่อดูที่ความถี่อื่น ๆ เราควรจะสามารถตรวจจับสถานการณ์ที่แปลกประหลาดนี้ได้เช่นกัน


4

แนวคิดพื้นฐานที่นี่คือการตรวจสอบสตริงย่อยที่มีศักยภาพเริ่มต้นที่ความยาว 1 และหยุดที่ครึ่งหนึ่งของความยาวของสตริงเดิม เราจะดูความยาวซับสตริงที่แบ่งความยาวสตริงเดิมเท่า ๆ กัน (เช่น str.length% substring.length == 0)

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

เราส่งคืน false เมื่อเราเรียกใช้สตริงย่อยที่อาจเกิดขึ้นเพื่อตรวจสอบ

function check(str) {
  const len = str.length;
  for (let subl = 1; subl <= len/2; ++subl) {
    if ((len % subl != 0) || str[0] != str[subl])
      continue;
    
    let i = 1;
    for (; i < subl; ++i)
    {
      let j = 0;
      for (; j < len; j += subl)
        if (str[i] != str[j + i])
          break;
      if (j != len)
        break;
    }
    
    if (i == subl)
      return true;
  }
  return false;
}

console.log(check('aa')) //true
console.log(check('aaa')) //true
console.log(check('abcabcabc')) //true
console.log(check('aba')) //false
console.log(check('ababa')) //false


-1

ฉันไม่คุ้นเคยกับ JavaScript ดังนั้นฉันจึงไม่ทราบว่าจะเกิดขึ้นได้เร็วแค่ไหน แต่นี่เป็นวิธีแก้ปัญหาเชิงเส้น (สมมติว่ามีการติดตั้งในตัว) โดยใช้ builtins ฉันจะอธิบายอัลกอริทึมใน pseudocode

function check(str) {
    t = str + str;
    find all overlapping occurrences of str in t;
    for each occurrence at position i
        if (i > 0 && i < str.length && str.length % i == 0)
            return true;  // str is a repetition of its first i characters
    return false;
}

แนวคิดนี้คล้ายกับคำตอบของ MBo สำหรับแต่ละตัวiที่หารความยาวstrจะเป็นการซ้ำซ้อนของiอักขระตัวแรกถ้าหากมันยังคงเหมือนเดิมหลังจากเปลี่ยนเป็นiอักขระ

มันอยู่ในใจของฉันว่า builtin อาจไม่พร้อมใช้งานหรือไม่มีประสิทธิภาพ ในกรณีนี้มันเป็นไปได้เสมอที่จะใช้อัลกอริทึม KMPด้วยตนเองซึ่งใช้รหัสในปริมาณเท่ากันกับอัลกอริทึมในคำตอบของ MBo


สหกรณ์ต้องการที่จะทราบว่าการทำซ้ำที่มีอยู่ บรรทัดที่สองของ (ส่วนของ) ฟังก์ชันของคุณนับจำนวนการทำซ้ำ - นั่นคือบิตที่ต้องอธิบาย เช่น "abcabcabc" มีการทำซ้ำ 3 ครั้งของ "abc" แต่บรรทัดที่สองของคุณทำงานอย่างไรว่ามีการทำซ้ำหรือไม่
ลอเรนซ์

@ ลอเรนซ์ฉันไม่เข้าใจคำถามของคุณ ขั้นตอนวิธีการนี้อยู่บนพื้นฐานความคิดที่ว่าสตริงเป็นซ้ำของสตริงย่อยของมันและถ้าหากสำหรับหารความยาวของมันบางi, หรือเท่ากันs[0:n-i] == s[i:n] s == s[i:n] + s[0:i]เหตุใดบรรทัดที่สองจึงจำเป็นต้องคิดออกว่ามีการทำซ้ำหรือไม่
infmagic2047

ให้ฉันดูว่าฉันเข้าใจอัลกอริทึมของคุณหรือไม่ ครั้งแรกที่คุณผนวกstrกับตัวเองในรูปแบบtแล้วสแกนtเพื่อพยายามหาภายในstr tโอเคสิ่งนี้สามารถใช้ได้ (ฉันถอนการโหวตของฉันไปแล้ว) แม้ว่ามันจะไม่เป็นเส้นตรงใน strlen (str) บอกว่าstrมีความยาว L จากนั้นในแต่ละตำแหน่ง p = 0,1,2, ... , ตรวจสอบว่า str [0..L-1] == t [p..p + L-1] ใช้ O (L เวลา) คุณต้องทำการตรวจสอบ O (L) เมื่อคุณผ่านค่าของ p ดังนั้นมันจึงเป็น O (L ^ 2)
ลอเรนซ์

-10

หนึ่งในแนวคิดง่ายๆคือการแทนที่สตริงด้วยสตริงย่อยของ "" และหากข้อความใด ๆ มีอยู่แล้วมันเป็นเท็จมิฉะนั้นก็เป็นจริง

'ababababa'.replace(/ab/gi,'')
"a" // return false
'abababab'.replace(/ab/gi,'')
 ""// return true


ใช่สำหรับ abc หรือ unicorn ผู้ใช้จะไม่ตรวจสอบด้วย / abc / หรือ / unicorn / ขออภัยหากฉันไม่มีบริบทของคุณ
Vinod kumar G

3
คำถามอาจชัดเจนขึ้น แต่สิ่งที่ขอเป็นวิธีการตัดสินใจว่าสตริงนั้นประกอบด้วยการซ้ำซ้อน 2 ครั้งขึ้นไปของสตริงอื่น ๆ อย่างสมบูรณ์หรือไม่ มันไม่ได้ค้นหาสตริงย่อยที่เฉพาะเจาะจง
HappyDog

2
ฉันได้เพิ่มความกระจ่างในคำถามซึ่งควรทำให้ชัดเจนยิ่งขึ้นในตอนนี้
HappyDog

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