Periksa sama ada Two Strings adalah Anagrams di Java

1. Gambaran keseluruhan

Menurut Wikipedia, anagram adalah kata atau frasa yang dibentuk dengan menyusun semula huruf dari kata atau frasa yang berbeza.

Kita dapat menggeneralisasikannya dalam pemprosesan tali dengan mengatakan bahawa anagram tali adalah rentetan lain dengan kuantiti yang sama dengan setiap watak di dalamnya, dalam urutan apa pun .

Dalam tutorial ini, kita akan melihat mengesan anagram string keseluruhan di mana kuantiti setiap watak mesti sama, termasuk watak bukan alpha seperti spasi dan digit. Contohnya, "! Rendah garam!" dan "burung hantu-lat !!" akan dianggap anagram kerana mengandungi watak yang sama persis.

2. Penyelesaian

Mari bandingkan beberapa penyelesaian yang dapat menentukan sama ada dua rentetan adalah anagram. Setiap penyelesaian akan memeriksa pada awal sama ada kedua-dua rentetan mempunyai bilangan watak yang sama. Ini adalah cara cepat untuk keluar lebih awal kerana input dengan panjang yang berbeza tidak boleh menjadi anagram .

Untuk setiap penyelesaian yang mungkin, mari kita lihat kerumitan pelaksanaan untuk kita sebagai pembangun. Kami juga akan melihat kerumitan waktu untuk CPU, menggunakan notasi O besar.

3. Periksa dengan Menyusun

Kita dapat menyusun semula watak setiap rentetan dengan menyusun wataknya, yang akan menghasilkan dua susunan watak yang dinormalisasi.

Sekiranya dua tali adalah anagram, bentuknya yang normal harus sama.

Di Jawa, pertama-tama kita dapat mengubah dua rentetan menjadi array char [] . Kemudian kita boleh menyusun dua tatasusunan ini dan memeriksa kesamaan:

boolean isAnagramSort(String string1, String string2) { if (string1.length() != string2.length()) { return false; } char[] a1 = string1.toCharArray(); char[] a2 = string2.toCharArray(); Arrays.sort(a1); Arrays.sort(a2); return Arrays.equals(a1, a2); } 

Penyelesaian ini mudah difahami dan dilaksanakan. Walau bagaimanapun, keseluruhan masa berjalan algoritma ini adalah O (n log n) kerana menyusun array watak n memerlukan masa O (n log n) .

Agar algoritma berfungsi, ia mesti menjadikan salinan kedua rentetan input sebagai susunan watak, menggunakan sedikit memori tambahan.

4. Periksa dengan Membilang

Strategi alternatif adalah dengan mengira jumlah kejadian setiap watak dalam input kami. Sekiranya histogram ini sama antara input, maka rentetan adalah anagram.

Untuk menjimatkan sedikit memori, mari kita bina satu histogram sahaja. Kami akan menambah jumlah bagi setiap watak dalam rentetan pertama, dan mengurangkan jumlah bagi setiap watak pada rentak kedua. Sekiranya kedua-dua rentetan itu adalah anagram, maka hasilnya ialah semuanya seimbang dengan 0

Histogram memerlukan jadual ukuran tetap dengan ukuran yang ditentukan oleh ukuran set watak. Sebagai contoh, jika kita hanya menggunakan satu bait untuk menyimpan setiap watak, maka kita dapat menggunakan ukuran array penghitungan 256 untuk menghitung kejadian setiap watak:

private static int CHARACTER_RANGE= 256; public boolean isAnagramCounting(String string1, String string2) { if (string1.length() != string2.length()) { return false; } int count[] = new int[CHARACTER_RANGE]; for (int i = 0; i < string1.length(); i++) { count[string1.charAt(i)]++; count[string2.charAt(i)]--; } for (int i = 0; i < CHARACTER_RANGE; i++) { if (count[i] != 0) { return false; } } return true; }

Penyelesaian ini lebih pantas dengan kerumitan masa O (n) . Walau bagaimanapun, ia memerlukan ruang tambahan untuk array penghitungan. Pada 256 bilangan bulat, untuk ASCII itu tidak terlalu buruk.

Walau bagaimanapun, jika kita perlu meningkatkan CHARACTER_RANGE untuk menyokong set watak berbilang bait seperti UTF-8, ini akan menjadi sangat ingatan. Oleh itu, sangat praktikal apabila bilangan watak yang mungkin berada dalam jarak yang kecil.

Dari sudut pandang pengembangan, penyelesaian ini mengandungi lebih banyak kod untuk dijaga dan kurang menggunakan fungsi perpustakaan Java.

5. Periksa dengan MultiSet

Kita dapat mempermudah proses penghitungan dan perbandingan dengan menggunakan MultiSet . MultiSet adalah koleksi yang menyokong kesamaan bebas pesanan dengan unsur pendua. Contohnya, multiset {a, a, b} dan {a, b, a} adalah sama.

Untuk menggunakan Multiset , pertama-tama kita perlu menambahkan kebergantungan Jambu biji ke fail pom.xml projek kita :

 com.google.guava guava 28.1-jre  

Kami akan menukar setiap rentetan input kami menjadi watak MultiSet . Kemudian kami akan memeriksa sama ada:

boolean isAnagramMultiset(String string1, String string2) { if (string1.length() != string2.length()) { return false; } Multiset multiset1 = HashMultiset.create(); Multiset multiset2 = HashMultiset.create(); for (int i = 0; i < string1.length(); i++) { multiset1.add(string1.charAt(i)); multiset2.add(string2.charAt(i)); } return multiset1.equals(multiset2); } 

Algoritma ini menyelesaikan masalah dalam masa O (n) tanpa perlu mengisytiharkan array pengiraan yang besar.

Ia serupa dengan penyelesaian pengiraan sebelumnya. Namun, daripada menggunakan jadual ukuran tetap untuk dihitung, kami memanfaatkan kelas MutlitSet untuk mensimulasikan jadual berukuran berubah-ubah, dengan jumlah untuk setiap watak.

Kod untuk penyelesaian ini menggunakan lebih banyak keupayaan perpustakaan peringkat tinggi daripada penyelesaian penghitungan kami.

6. Anagram berdasarkan huruf

Contohnya setakat ini tidak mematuhi definisi linguistik anagram. Ini kerana mereka menganggap watak tanda baca sebagai sebahagian daripada anagram, dan huruf ini sensitif.

Mari sesuaikan algoritma untuk mengaktifkan anagram berdasarkan huruf. Mari kita pertimbangkan penyusunan semula huruf yang tidak peka huruf besar-kecil, tanpa mengira watak lain seperti ruang kosong dan tanda baca. Contohnya, "Titik perpuluhan" dan "Saya titik di tempat." akan menjadi anagram antara satu sama lain.

Untuk menyelesaikan masalah ini, kita dapat memproses dua rentetan input terlebih dahulu untuk menyaring watak yang tidak diingini dan menukar huruf menjadi huruf kecil. Kemudian kita boleh menggunakan salah satu penyelesaian di atas (katakanlah, penyelesaian MultiSet ) untuk memeriksa anagram pada rentetan yang diproses:

String preprocess(String source) { return source.replaceAll("[^a-zA-Z]", "").toLowerCase(); } boolean isLetterBasedAnagramMultiset(String string1, String string2) { return isAnagramMultiset(preprocess(string1), preprocess(string2)); }

Pendekatan ini boleh menjadi cara umum untuk menyelesaikan semua varian masalah anagram. Sebagai contoh, jika kita juga ingin memasukkan digit, kita hanya perlu menyesuaikan penapis praprosesan.

7. Kesimpulannya

Dalam artikel ini, kami melihat tiga algoritma untuk memeriksa sama ada rentetan yang diberikan adalah anagram yang lain, watak untuk watak. Untuk setiap penyelesaian, kami membincangkan pertukaran antara kelajuan, kebolehbacaan, dan ukuran memori yang diperlukan.

Kami juga melihat bagaimana menyesuaikan algoritma untuk memeriksa anagram dalam pengertian linguistik yang lebih tradisional. Kami mencapai ini dengan memproses input ke dalam huruf kecil.

Seperti biasa, kod sumber untuk artikel tersebut terdapat di GitHub.