Seorang teman saya ditanya pertanyaan berikut hari ini di wawancara untuk posisi pengembang perangkat lunak:
Mengingat dua string yang s1
dan s2
bagaimana Anda akan memeriksa apakah s1
merupakan diputar versi s2
?
Contoh:
Jika s1 = "stackoverflow"
kemudian berikut ini adalah beberapa versi rotasinya:
"tackoverflows"
"ackoverflowst"
"overflowstack"
dimana "stackoverflwo"
adalah tidak versi yang diputar.
Jawaban yang dia berikan adalah:
Ambil
s2
dan temukan awalan terpanjang yang merupakan sub strings1
, yang akan memberi Anda titik rotasi. Setelah Anda menemukan titik itu, istirahats2
pada titik itu untuk mendapatkans2a
dans2b
, lalu periksa apakahconcatenate(s2a,s2b) == s1
Sepertinya solusi yang baik untuk saya dan teman saya. Tetapi pewawancara berpikir sebaliknya. Dia meminta solusi yang lebih sederhana. Tolong bantu saya dengan mengatakan bagaimana Anda melakukan ini Java/C/C++
?
Terima kasih sebelumnya.
Jawaban:
Pertama pastikan
s1
dans2
memiliki panjang yang sama. Kemudian periksa untuk melihat apakahs2
substring daris1
digabungkan dengans1
:Di Jawa:
sumber
(s1+s1).contains(s2)
di Jawa.s1+s1
. Jelas, semua substringnya dengan ukurans1.length
adalah rotasis1
, oleh konstruksi. Oleh karena itu, setiap string ukurans1.length
yang merupakan substrings1+s1
harus berupa rotasis1
.Tentunya jawaban yang lebih baik adalah, "Yah, saya akan bertanya kepada komunitas stackoverflow dan mungkin akan memiliki setidaknya 4 jawaban yang benar-benar bagus dalam 5 menit". Otak baik dan semuanya, tetapi saya akan memberi nilai lebih tinggi pada seseorang yang tahu bagaimana bekerja dengan orang lain untuk mendapatkan solusi.
sumber
Contoh python lain (berdasarkan jawaban THE):
sumber
s2
daripadas1
terlalu ... kemudian menyadari bahwa hubungannya memang simetris.in
operator tidak menggunakan algoritma O (n)?s1 in s2
dioptimalkan. Lihat effbot.org/zone/stringlib.htm untuk deskripsi algoritme. Google tampaknya menunjukkan bahwa Java tidak memiliki pencarian string cepat (lihat johannburkard.de/software/stringsearch misalnya) meskipun saya ragu itu akan merusak apa pun jika mereka mengubahnya.Karena orang lain telah mengirimkan solusi kompleksitas waktu terburuk kuadratik, saya akan menambahkan yang linear (berdasarkan Algoritma KMP ):
contoh kerja
sumber
EDIT: Jawaban yang diterima jelas lebih elegan dan efisien daripada ini, jika Anda menemukannya. Saya meninggalkan jawaban ini sebagai apa yang akan saya lakukan jika saya tidak berpikir untuk menggandakan string aslinya.
Aku hanya akan memaksanya dengan kasar. Periksa panjangnya terlebih dahulu, dan kemudian coba setiap pergantian rotasi yang dimungkinkan. Jika tidak ada yang berhasil, kembalikan salah - jika ada yang benar, kembalikan benar segera.
Tidak ada kebutuhan khusus untuk menggabungkan - cukup gunakan pointer (C) atau indeks (Jawa) dan berjalan bersama, satu di setiap string - mulai dari awal satu string dan rotasi kandidat saat ini diimbangi pada string kedua, dan membungkus jika perlu . Periksa persamaan karakter di setiap titik dalam string. Jika Anda sampai ke akhir string pertama, Anda sudah selesai.
Mungkin akan lebih mudah untuk digabungkan - meskipun mungkin kurang efisien, setidaknya di Jawa.
sumber
Inilah satu menggunakan regex hanya untuk bersenang-senang:
Anda dapat membuatnya sedikit lebih sederhana jika Anda dapat menggunakan karakter pembatas khusus yang dijamin tidak berada di kedua string.
Anda juga dapat menggunakan lookbehind dengan pengulangan terbatas:
sumber
Whoa, whoa ... mengapa semua orang sangat senang dengan
O(n^2)
jawaban? Saya yakin kita bisa melakukan yang lebih baik di sini. THE THE jawaban di atas termasukO(n)
operasi dalam satuO(n)
lingkaran (panggilan substring / indexOf). Bahkan dengan algoritma pencarian yang lebih efisien; katakanBoyer-Moore
atauKMP
, kasus terburuk masihO(n^2)
dengan duplikat.Sebuah
O(n)
jawaban yang acak adalah mudah; ambil hash (seperti sidik jari Rabin) yang mendukungO(1)
jendela geser; string hash 1, kemudian string hash 2, dan lanjutkan untuk memindahkan jendela hash 1 di sekitar string dan melihat apakah fungsi hash bertabrakan.Jika kita membayangkan kasus terburuk adalah sesuatu seperti "memindai dua untai DNA", maka kemungkinan tabrakan meningkat, dan ini mungkin merosot menjadi sesuatu seperti
O(n^(1+e))
atau sesuatu (hanya menebak di sini).Akhirnya, ada
O(nlogn)
solusi deterministik yang memiliki konstanta yang sangat besar di luar. Pada dasarnya, idenya adalah mengambil konvolusi dari dua string. Nilai maksimal konvolusi adalah perbedaan rotasi (jika diputar); sebuahO(n)
cek mengonfirmasi. Yang menyenangkan adalah jika ada dua nilai maks yang sama, maka keduanya merupakan solusi yang valid. Anda dapat melakukan konvolusi dengan dua produk FFT dan dot, dan iFFT, jadinlogn + nlogn + n + nlogn + n == O(nlogn)
.Karena Anda tidak dapat membuat angka nol, dan Anda tidak dapat menjamin bahwa string memiliki panjang 2 ^ n, FFT tidak akan menjadi yang cepat; mereka akan menjadi yang lambat, masih
O(nlogn)
tetapi konstanta yang jauh lebih besar daripada algoritma CT.Semua yang dikatakan, saya benar-benar, 100% positif bahwa ada
O(n)
solusi deterministik di sini, tetapi terkutuk jika saya dapat menemukannya.sumber
%stringsize
) dijamin menjadi waktu linier.Tinju, pastikan 2 senar memiliki panjang yang sama. Kemudian di C, Anda bisa melakukan ini dengan iterasi pointer sederhana.
sumber
Di sini ada
O(n)
dan di tempat alghoritm. Ini menggunakan<
operator untuk elemen string. Tentu saja itu bukan milikku. Saya mengambilnya dari sini (Situs ini dalam bahasa Polandia. Saya menemukannya sekali di masa lalu dan saya tidak dapat menemukan sesuatu seperti itu sekarang dalam bahasa Inggris, jadi saya menunjukkan apa yang saya miliki :)).sumber
Saya kira lebih baik melakukan ini di
Java
:Dalam Perl saya akan melakukan:
atau bahkan lebih baik menggunakan fungsi indeks daripada regex:
sumber
\Q
di/\Q$string2/
.\Q
mengutip setiap karakter khusus di$string2
. Tanpanya,.
akan dianggap sebagai rotasi dari string 1 karakter.Tidak yakin apakah ini adalah metode yang paling efisien, tetapi mungkin relatif menarik : transformasi Burrows-Wheeler . Menurut artikel WP, semua rotasi input menghasilkan output yang sama. Untuk aplikasi seperti kompresi ini tidak diinginkan, jadi rotasi aslinya ditunjukkan (misalnya dengan indeks; lihat artikel). Tetapi untuk perbandingan rotasi-independen sederhana, ini terdengar ideal. Tentu saja, itu belum tentu efisien secara ideal!
sumber
Ambil setiap karakter sebagai amplitudo dan lakukan transformasi Fourier diskrit pada mereka. Jika mereka berbeda hanya dengan rotasi, spektrum frekuensi akan sama dengan kesalahan pembulatan. Tentu saja ini tidak efisien kecuali panjangnya adalah kekuatan 2 sehingga Anda dapat melakukan FFT :-)
sumber
Belum ada yang menawarkan pendekatan modulo, jadi inilah salah satu:
Keluaran:
[EDIT: 2010-04-12]
piotr memperhatikan cacat pada kode saya di atas. Kesalahan saat karakter pertama dalam string muncul dua kali atau lebih. Sebagai contoh,
stackoverflow
diuji terhadapowstackoverflow
menghasilkan false, padahal seharusnya benar.Terima kasih piotr karena menemukan kesalahan.
Sekarang, inilah kode yang diperbaiki:
Inilah hasilnya:
Inilah pendekatan lambda:
Inilah output pendekatan lambda:
sumber
Karena tidak ada yang memberikan solusi C ++. ini dia:
sumber
Trik rotasi pointer sederhana Opera berfungsi, tetapi sangat tidak efisien dalam kasus terburuk dalam menjalankan waktu. Cukup bayangkan sebuah string dengan banyak karakter berulang yang panjang, yaitu:
"Lingkaran sampai ada ketidaksesuaian, lalu bertambah satu dan coba lagi" adalah pendekatan yang mengerikan, secara komputasi.
Untuk membuktikan bahwa Anda dapat melakukan pendekatan penyatuan di dataran C tanpa terlalu banyak usaha, berikut adalah solusi saya:
Ini linier dalam menjalankan waktu, dengan mengorbankan penggunaan memori O (n) dalam overhead.
(Perhatikan bahwa implementasi strstr () adalah platform-spesifik, tetapi jika mati otak, selalu dapat diganti dengan alternatif yang lebih cepat seperti algoritma Boyer-Moore)
sumber
strstr()
di O (n + m)? Juga, jika standar (atau apa pun) tidak menjamin Anda waktu berjalan linierstrstr()
, Anda tidak dapat menyatakan bahwa keseluruhan algoritma memiliki waktu linear yang linier.s1SelfConcat
: hanya sejak C9x C memungkinkan ukuran array variabel (meskipun GCC telah memperbolehkannya lebih lama), dan Anda akan mengalami kesulitan mengalokasikan string besar pada stack. Yosef Kreinin menulis posting blog yang sangat lucu tentang masalah ini. Juga, solusi Anda masih kuadratik dengan Boyer-Moore; Anda menginginkan KMP.C #:
sumber
Saya suka jawaban yang memeriksa apakah s2 adalah substring dari s1 yang disatukan dengan s1.
Saya ingin menambahkan pengoptimalan yang tidak kehilangan keanggunannya.
Alih-alih menyatukan string, Anda dapat menggunakan tampilan bergabung (saya tidak tahu bahasa lain, tetapi untuk C ++ Boost.Range memberikan pandangan seperti itu).
Sebagai cek jika string adalah substring dari yang lain memiliki kompleksitas rata-rata linier (Kompleksitas terburuk adalah kuadrat), optimasi ini harus meningkatkan kecepatan dengan faktor 2 rata-rata.
sumber
Jawaban Java murni (tidak dapat memeriksa)
sumber
Dan sekarang untuk sesuatu yang sama sekali berbeda.
Jika Anda menginginkan jawaban yang sangat cepat dalam konteks terbatas ketika string tidak dirotasi satu sama lain
Setuju, itu bisa gagal, tetapi sangat cepat untuk mengatakan jika string tidak cocok dan jika mereka cocok, Anda masih dapat menggunakan algoritma lain seperti penggabungan string untuk memeriksa.
sumber
Solusi Ruby lain berdasarkan pada jawaban:
sumber
Sangat mudah untuk menulis dalam PHP menggunakan
strlen
danstrpos
fungsi:Saya tidak tahu apa yang
strpos
digunakan secara internal, tetapi jika menggunakan KMP ini akan linear dalam waktu.sumber
Balikkan salah satu senarnya. Ambil FFT keduanya (memperlakukan mereka sebagai urutan sederhana bilangan bulat). Lipat gandakan hasilnya bersamaan. Transformasikan kembali menggunakan FFT terbalik. Hasilnya akan memiliki puncak tunggal jika string adalah rotasi satu sama lain - posisi puncak akan menunjukkan seberapa besar mereka diputar sehubungan satu sama lain.
sumber
Kenapa tidak seperti ini?
Tentu saja, Anda dapat menulis fungsi IndexOf () Anda sendiri; Saya tidak yakin apakah .NET menggunakan cara yang naif atau lebih cepat.
Naif:
Lebih cepat:
Sunting: Saya mungkin memiliki beberapa masalah satu-persatu; tidak ingin memeriksa. ;)
sumber
Saya akan melakukan ini di Perl :
sumber
sumber
Bergabunglah
string1
denganstring2
dan gunakan algoritma KMP untuk memeriksa apakahstring2
ada dalam string yang baru dibentuk. Karena kompleksitas waktu KMP lebih rendah daripadasubstr
.sumber