Saya harus membuat fungsi yang mengambil string, dan itu harus kembali true
atau false
didasarkan pada apakah input terdiri dari urutan karakter yang diulang. Panjang string yang diberikan selalu lebih besar dari 1
dan urutan karakter harus memiliki setidaknya satu pengulangan.
"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)
Saya telah membuat fungsi di bawah ini:
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
Memeriksa ini adalah bagian dari masalah sebenarnya. Saya tidak mampu membeli solusi yang tidak efisien seperti ini. Pertama-tama, ia berputar melalui setengah dari string.
Masalah kedua adalah bahwa ia menggunakan replace()
di setiap loop yang membuatnya lambat. Apakah ada solusi yang lebih baik mengenai kinerja?
javascript
string
algorithm
Maheer Ali
sumber
sumber
Jawaban:
Ada teorema kecil yang bagus tentang string seperti ini.
Di sini, rotasi berarti menghapus sejumlah karakter dari depan string dan memindahkannya ke belakang. Misalnya, string
hello
dapat diputar untuk membentuk salah satu dari string ini:Untuk melihat mengapa ini bekerja, pertama, asumsikan bahwa suatu string terdiri dari k salinan berulang dari string w. Kemudian menghapus salinan pertama dari pola yang berulang (w) dari depan string dan menempelkannya ke belakang akan mengembalikan string yang sama. Arah sebaliknya agak sulit untuk dibuktikan, tetapi idenya adalah bahwa jika Anda memutar string dan mendapatkan kembali apa yang Anda mulai, Anda dapat menerapkan rotasi itu berulang kali untuk memasang string dengan beberapa salinan dari pola yang sama (pola yang menjadi string yang Anda butuhkan untuk pindah ke ujung untuk melakukan rotasi).
Sekarang pertanyaannya adalah bagaimana memeriksa apakah ini masalahnya. Untuk itu, ada teorema indah lain yang bisa kita gunakan:
Sebagai contoh, kita dapat melihat bahwa itu
lohel
adalah rotasihello
sebagai berikut:Dalam kasus kami, kami tahu bahwa setiap string x akan selalu menjadi substring dari xx (itu akan muncul dua kali, sekali pada setiap salinan x). Jadi pada dasarnya kita hanya perlu memeriksa apakah string x adalah substring dari xx tanpa membiarkannya cocok dengan karakter pertama atau setengah. Berikut ini satu kalimat untuk itu:
Dengan asumsi
indexOf
diimplementasikan menggunakan algoritma pencocokan string cepat, ini akan berjalan dalam waktu O (n), di mana n adalah panjang dari string input.Semoga ini membantu!
sumber
Anda dapat melakukannya dengan menangkap grup dan referensi kembali . Periksa saja pengulangan dari nilai yang ditangkap pertama.
Di RegExp di atas:
^
dan$
singkatan dari awal dan akhir jangkar untuk memprediksi posisi.(.+)
menangkap pola apa pun dan menangkap nilai (kecuali\n
).\1
adalah referensi balik dari nilai yang ditangkap pertama dan\1+
akan memeriksa pengulangan nilai yang ditangkap.Penjelasan regex di sini
Untuk penggunaan debug RegExp: https://regex101.com/r/pqlAuP/1/debugger
Kinerja: https://jsperf.com/reegx-and-loop/13
sumber
If you use normal (TCS:no backreference, concatenation,alternation,Kleene star) regexp and regexp is already compiled then it's O(n).
tetapi ketika Anda menulis, Anda menggunakan referensi-ulang, jadi apakah masih O (n)?[\s\S]
alih-alih.
jika Anda harus mencocokkan karakter baris baru dengan cara yang sama seperti karakter lainnya. Karakter titik tidak cocok dengan baris baru; pencarian alternatif untuk semua karakter white-space dan non-whitespace, yang berarti bahwa baris baru termasuk dalam pertandingan. (Perhatikan bahwa ini lebih cepat daripada yang lebih intuitif(.|[\r\n])
.) Namun, jika string jelas tidak mengandung baris baru, maka yang sederhana.
akan lebih cepat. Catatan ini akan jauh lebih sederhana jika flag dotall diimplementasikan./^(.+?)\1+$/
sedikit lebih cepat? (12 langkah vs 20 langkah)Mungkin pendekatan algoritmik tercepat adalah membangun fungsi-Z dalam waktu linier:
Implementasi C ++ untuk referensi:
Implementasi JavaScript
Menambahkan optimasi - membangun setengah z-array dan keluar awal
Maka Anda perlu memeriksa indeks
i
yang membagi n. Jika Anda menemukani
itui+z[i]=n
maka strings
dapat dikompresi dengan panjangi
dan Anda dapat kembalitrue
.Misalnya, untuk
z-array adalah
dan kita dapat menemukannya untuk
jadi
s
mungkin direpresentasikan sebagai substring dengan panjang 4 diulang tiga kali.sumber
return z.some((zi, i) => (i + zi) === n && n % i === 0)
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; }
Saya membaca jawaban gnasher729 dan mengimplementasikannya. Idenya adalah bahwa jika ada pengulangan, maka harus ada (juga) jumlah pengulangan utama.
Algoritma yang sedikit berbeda adalah ini:
Saya telah memperbarui halaman jsPerf yang berisi algoritma yang digunakan pada halaman ini.
sumber
function*
untuk pertama kalinya seperti saya, ini untuk mendeklarasikan generator, bukan fungsi biasa. Lihat MDNAsumsikan string S memiliki panjang N dan terbuat dari duplikat substring s, maka panjang s membagi N. Misalnya, jika S memiliki panjang 15, maka substring memiliki panjang 1, 3, atau 5.
Biarkan S dibuat dari (p * q) salinan s. Kemudian S juga terbuat dari salinan p (s, diulang q kali). Karena itu kami memiliki dua kasus: Jika N adalah prima atau 1, maka S hanya dapat dibuat dari salinan panjang substring 1. Jika N adalah komposit, maka kita hanya perlu memeriksa substring s dengan panjang N / p untuk primes p membagi panjang S.
Jadi tentukan N = panjang S, lalu temukan semua faktor prima dalam waktu O (sqrt (N)). Jika hanya ada satu faktor N, periksa apakah S adalah string yang sama diulang N kali, jika tidak untuk setiap faktor prima p, periksa apakah S terdiri dari p pengulangan karakter N / p pertama.
sumber
Saya pikir fungsi rekursif mungkin sangat cepat juga. Pengamatan pertama adalah bahwa panjang pola maksimum yang diulang adalah setengah panjang total string. Dan kita bisa menguji semua kemungkinan panjang pola yang berulang: 1, 2, 3, ..., str.length / 2
Fungsi rekursif isRepeating (p, str) menguji jika pola ini diulang dalam str.
Jika str lebih panjang dari pola, rekursi membutuhkan bagian pertama (panjang yang sama dengan p) untuk menjadi pengulangan serta sisa str. Jadi str secara efektif dipecah menjadi potongan-potongan panjang p.length.
Jika pola dan str yang diuji memiliki ukuran yang sama, rekursi berakhir di sini, berhasil.
Jika panjangnya berbeda (terjadi untuk "aba" dan pola "ab") atau jika potongannya berbeda, maka false dikembalikan, menyebarkan rekursi.
Kinerja: https://jsperf.com/reegx-and-loop/13
sumber
if( str===p.repeat(str.length/i) ) return true;
daripada menggunakan fungsi rekursif?Tulis ini dalam Python. Saya tahu itu bukan platform, tetapi butuh waktu 30 menit. PS => PYTHON
sumber
Pendekatan saya mirip dengan gnasher729, karena menggunakan panjang potensial substring sebagai fokus utama, tetapi kurang matematika-y dan proses intensif:
L: Panjang string asli
S: Panjang potensial dari sub-string yang valid
Loop S dari (bagian integer) L / 2 ke 1. Jika L / S adalah integer, periksa string asli Anda terhadap karakter S fist dari string asli yang diulangi kali L / S.
Alasan untuk pengulangan dari L / 2 mundur dan tidak dari 1 dan seterusnya adalah untuk mendapatkan substring terbesar. Jika Anda ingin loop substring sekecil mungkin dari 1 hingga L / 2. Contoh: "abababab" memiliki "ab" dan "abab" sebanyak mungkin substring. Manakah dari keduanya akan lebih cepat jika Anda hanya peduli tentang hasil benar / salah tergantung pada jenis string / substring ini akan diterapkan.
sumber
Kode Mathematica berikut hampir mendeteksi jika daftar diulang setidaknya sekali. Jika string diulang setidaknya satu kali, ia mengembalikan nilai true, tetapi mungkin juga mengembalikan nilai true jika string adalah kombinasi linear dari string yang berulang.
Kode ini mencari kontribusi "full-length", yang harus nol dalam string berulang, tetapi string
accbbd
juga dianggap diulang, karena merupakan jumlah dari dua string berulangababab
dan012012
.Idenya adalah menggunakan Fast Fourier Transform, dan mencari spektrum frekuensi. Dengan melihat frekuensi lain, seseorang seharusnya dapat mendeteksi skenario aneh ini juga.
sumber
Ide dasar di sini adalah untuk memeriksa setiap substring potensial, mulai dari panjang 1 dan berhenti di setengah dari panjang string asli. Kami hanya melihat panjang substring yang membagi panjang string asli secara merata (mis. Str.length% substring.length == 0).
Implementasi ini melihat karakter pertama dari setiap kemungkinan pengulangan substring sebelum pindah ke karakter kedua, yang mungkin menghemat waktu jika substring diharapkan panjang. Jika tidak ada ketidakcocokan yang ditemukan setelah memeriksa seluruh substring, maka kami mengembalikan true.
Kami mengembalikan false ketika kami kehabisan substring potensial untuk memeriksa.
sumber
Saya tidak terbiasa dengan JavaScript, jadi saya tidak tahu seberapa cepat ini akan terjadi, tapi di sini adalah solusi waktu linier (dengan asumsi implementasi builtin yang masuk akal) hanya menggunakan builtin. Saya akan menjelaskan algoritma dalam pseudocode.
Idenya mirip dengan jawaban MBo. Untuk setiap
i
yang membagi panjang,str
adalah pengulangani
karakter pertama jika dan hanya jika tetap sama setelah beralih untuki
karakter.Terlintas dalam pikiran saya bahwa builtin seperti itu mungkin tidak tersedia atau tidak efisien. Dalam hal ini, selalu dimungkinkan untuk mengimplementasikan algoritma KMP secara manual, yang membutuhkan jumlah kode yang sama dengan algoritma dalam jawaban MBo.
sumber
i
,s[0:n-i] == s[i:n]
atau ekuivalen,s == s[i:n] + s[0:i]
. Mengapa baris kedua perlu dipecahkan apakah ada pengulangan?str
sendiri untuk membentukt
, kemudian memindait
untuk mencoba menemukanstr
di dalamnyat
. Oke, ini bisa berhasil (Saya telah menarik kembali downvote saya). Ini tidak linier dalam strlen (str), meskipun. Katakanlahstr
panjang L. Kemudian pada setiap posisi p = 0,1,2, ..., memeriksa apakah str [0..L-1] == t [p..p + L-1] mengambil O (L ) waktu. Anda perlu melakukan pemeriksaan O (L) saat Anda melihat nilai-nilai p, jadi O (L ^ 2).Salah satu ide sederhana adalah mengganti string dengan substring "" dan jika ada teks maka itu salah, kalau tidak itu benar.
sumber