Как проверить, являются ли два слова анаграммами
У меня есть программа, которая показывает, являются ли два слова анаграммами друг друга. Есть несколько примеров, которые не будут работать должным образом, и я был бы признателен за любую помощь, хотя если бы она не была продвинута, это было бы здорово, так как я программист 1-го года. "учитель" и "theclassroom" являются анаграммами друг друга, однако, когда я изменить "theclassroom" в "theclafsroom" он по-прежнему говорит, что они анаграмм, что я делаю не так?
import java.util.ArrayList;
public class AnagramCheck
{
public static void main(String args[])
{
String phrase1 = "tbeclassroom";
phrase1 = (phrase1.toLowerCase()).trim();
char[] phrase1Arr = phrase1.toCharArray();
String phrase2 = "schoolmaster";
phrase2 = (phrase2.toLowerCase()).trim();
ArrayList<Character> phrase2ArrList = convertStringToArraylist(phrase2);
if (phrase1.length() != phrase2.length())
{
System.out.print("There is no anagram present.");
}
else
{
boolean isFound = true;
for (int i=0; i<phrase1Arr.length; i++)
{
for(int j = 0; j < phrase2ArrList.size(); j++)
{
if(phrase1Arr[i] == phrase2ArrList.get(j))
{
System.out.print("There is a common element.n");
isFound = ;
phrase2ArrList.remove(j);
}
}
if(isFound == false)
{
System.out.print("There are no anagrams present.");
return;
}
}
System.out.printf("%s is an anagram of %s", phrase1, phrase2);
}
}
public static ArrayList<Character> convertStringToArraylist(String str) {
ArrayList<Character> charList = new ArrayList<Character>();
for(int i = 0; i<str.length();i++){
charList.add(str.charAt(i));
}
return charList;
}
}
30 ответов
самый быстрый алгоритм-сопоставить каждый из 26 английских символов с уникальным простым числом. Затем вычислите произведение строки. По фундаментальной теореме арифметики 2 строки являются анаграммами тогда и только тогда, когда их произведения одинаковы.
два слова являются анаграммами друг друга, если они содержат одинаковое количество символов и те же персонажи. Вам нужно только отсортировать символы в лексикографическом порядке и сравнить, если строка a равна строке b на всех шагах.
вот пример кода. Посмотрите в Arrays
в API, чтобы понять, что здесь происходит.
public boolean isAnagram(String firstWord, String secondWord) {
char[] word1 = firstWord.replaceAll("[\s]", "").toCharArray();
char[] word2 = secondWord.replaceAll("[\s]", "").toCharArray();
Arrays.sort(word1);
Arrays.sort(word2);
return Arrays.equals(word1, word2);
}
Если вы сортируете любой массив, решение становится O (N log n). но если вы используете hashmap, Это O (n). проверено и работает.
char[] word1 = "test".toCharArray();
char[] word2 = "tes".toCharArray();
Map<Character, Integer> lettersInWord1 = new HashMap<Character, Integer>();
for (char c : word1) {
int count = 1;
if (lettersInWord1.containsKey(c)) {
count = lettersInWord1.get(c) + 1;
}
lettersInWord1.put(c, count);
}
for (char c : word2) {
int count = -1;
if (lettersInWord1.containsKey(c)) {
count = lettersInWord1.get(c) - 1;
}
lettersInWord1.put(c, count);
}
for (char c : lettersInWord1.keySet()) {
if (lettersInWord1.get(c) != 0) {
return false;
}
}
return true;
вот простое быстрое решение O (n) без использования сортировки или нескольких циклов или хэш-карт. Мы увеличиваем количество каждого символа в первом массиве и уменьшить количество каждого символа в массиве. Если результирующий массив counts полон нулей, строки являются анаграммами. Может быть расширен для включения других символов путем увеличения размера массива counts.
class AnagramsFaster{
private static boolean compare(String a, String b){
char[] aArr = a.toLowerCase().toCharArray(), bArr = b.toLowerCase().toCharArray();
if (aArr.length != bArr.length)
return false;
int[] counts = new int[26]; // An array to hold the number of occurrences of each character
for (int i = 0; i < aArr.length; i++){
counts[aArr[i]-97]++; // Increment the count of the character at i
counts[bArr[i]-97]--; // Decrement the count of the character at i
}
// If the strings are anagrams, the counts array will be full of zeros
for (int i = 0; i<26; i++)
if (counts[i] != 0)
return false;
return true;
}
public static void main(String[] args){
System.out.println(compare(args[0], args[1]));
}
}
многие люди представили решения, но я просто хочу поговорить об алгоритмической сложности некоторых из общих подходов:
простой " сортировка символов с помощью
Arrays.sort()
" подход будетO(N log N)
.если вы используете сортировку radix, это сводится к
O(N)
СO(M)
пространство, гдеM
- это количество различных символов в алфавите. (Это 26 на английском языке ... но теоретически мы должны рассмотрим многоязычные анаграммы.)"подсчет символов" с использованием массива подсчетов также
O(N)
... и быстрее, чем сортировка radix, потому что вам не нужно восстанавливать отсортированную строку. Использование пространства будетO(M)
."подсчет символов" с помощью словаря, hashmap, treemap или эквивалента будет медленнее, чем подход массива, если алфавит не огромен.
-
элегантный подход "продукт простых чисел", к сожалению,
O(N^2)
в худшем случае это потому, что для достаточно длинных слов или фраз произведение простых чисел не будет вписываться вlong
. Это означает, что вам нужно использоватьBigInteger
, и N раз умножая aBigInteger
небольшая константаO(N^2)
.для гипотетического Большого алфавита коэффициент масштабирования будет большим. В худшем случае использование пространства для хранения произведения простых чисел как
BigInteger
(Я думаю)O(N*logM)
. A
hashcode
подход, основанный обычноO(N)
Если слова не являются анаграммами. Если хеш-коды равны, то вам все равно нужно сделать правильный тест анаграммы. Так что это не полное решение.
o (n) решение без какой-либо сортировки и с использованием только одной карты.
public boolean isAnagram(String leftString, String rightString) {
if (leftString == null || rightString == null) {
return false;
} else if (leftString.length() != rightString.length()) {
return false;
}
Map<Character, Integer> occurrencesMap = new HashMap<>();
for(int i = 0; i < leftString.length(); i++){
char charFromLeft = leftString.charAt(i);
int nrOfCharsInLeft = occurrencesMap.containsKey(charFromLeft) ? occurrencesMap.get(charFromLeft) : 0;
occurrencesMap.put(charFromLeft, ++nrOfCharsInLeft);
char charFromRight = rightString.charAt(i);
int nrOfCharsInRight = occurrencesMap.containsKey(charFromRight) ? occurrencesMap.get(charFromRight) : 0;
occurrencesMap.put(charFromRight, --nrOfCharsInRight);
}
for(int occurrencesNr : occurrencesMap.values()){
if(occurrencesNr != 0){
return false;
}
}
return true;
}
и менее общее решение, но немного быстрее. Вы должны разместить свой алфавит здесь:
public boolean isAnagram(String leftString, String rightString) {
if (leftString == null || rightString == null) {
return false;
} else if (leftString.length() != rightString.length()) {
return false;
}
char letters[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
Map<Character, Integer> occurrencesMap = new HashMap<>();
for (char l : letters) {
occurrencesMap.put(l, 0);
}
for(int i = 0; i < leftString.length(); i++){
char charFromLeft = leftString.charAt(i);
Integer nrOfCharsInLeft = occurrencesMap.get(charFromLeft);
occurrencesMap.put(charFromLeft, ++nrOfCharsInLeft);
char charFromRight = rightString.charAt(i);
Integer nrOfCharsInRight = occurrencesMap.get(charFromRight);
occurrencesMap.put(charFromRight, --nrOfCharsInRight);
}
for(Integer occurrencesNr : occurrencesMap.values()){
if(occurrencesNr != 0){
return false;
}
}
return true;
}
мы идем по двум строкам одинаковой длины и отслеживаем различия между ними. Нам все равно, в чем разница, мы просто хотим знать, имеют ли они одинаковые символы или нет. Мы можем сделать это в O (n/2) без какой-либо постобработки (или много простых чисел).
public class TestAnagram {
public static boolean isAnagram(String first, String second) {
String positive = first.toLowerCase();
String negative = second.toLowerCase();
if (positive.length() != negative.length()) {
return false;
}
int[] counts = new int[26];
int diff = 0;
for (int i = 0; i < positive.length(); i++) {
int pos = (int) positive.charAt(i) - 97; // convert the char into an array index
if (counts[pos] >= 0) { // the other string doesn't have this
diff++; // an increase in differences
} else { // it does have it
diff--; // a decrease in differences
}
counts[pos]++; // track it
int neg = (int) negative.charAt(i) - 97;
if (counts[neg] <= 0) { // the other string doesn't have this
diff++; // an increase in differences
} else { // it does have it
diff--; // a decrease in differences
}
counts[neg]--; // track it
}
return diff == 0;
}
public static void main(String[] args) {
System.out.println(isAnagram("zMarry", "zArmry")); // true
System.out.println(isAnagram("basiparachromatin", "marsipobranchiata")); // true
System.out.println(isAnagram("hydroxydeoxycorticosterones", "hydroxydesoxycorticosterone")); // true
System.out.println(isAnagram("hydroxydeoxycorticosterones", "hydroxydesoxycorticosterons")); // false
System.out.println(isAnagram("zArmcy", "zArmry")); // false
}
}
Да этот код зависит от набора символов ASCII English в нижнем регистре, но его не должно быть сложно изменить на другие языки. Вы всегда можете использовать карту[Character, Int] для отслеживания того же информация, она будет медленнее.
используя больше памяти (хэш-карту не более N/2 элементов), нам не нужно сортировать строки.
public static boolean areAnagrams(String one, String two) {
if (one.length() == two.length()) {
String s0 = one.toLowerCase();
String s1 = two.toLowerCase();
HashMap<Character, Integer> chars = new HashMap<Character, Integer>(one.length());
Integer count;
for (char c : s0.toCharArray()) {
count = chars.get(c);
count = Integer.valueOf(count != null ? count + 1 : 1);
chars.put(c, count);
}
for (char c : s1.toCharArray()) {
count = chars.get(c);
if (count == null) {
return false;
} else {
count--;
chars.put(c, count);
}
}
for (Integer i : chars.values()) {
if (i != 0) {
return false;
}
}
return true;
} else {
return false;
}
}
эта функция фактически работает в O (N) ... вместо O (NlogN) для решения, которое сортирует строки. Если бы я предположил, что вы собираетесь использовать только алфавитные символы, я мог бы использовать только массив из 26 ints (от a до z без акцентов или украшений) вместо hashmap.
Если мы определяем, что : Н = |один| + |два| мы делаем одну итерацию над N (один раз над одним, чтобы увеличить счетчики, и один раз, чтобы уменьшить их более двух). Затем, чтобы проверить итоги, мы перебираем в mose N / 2.
другие описанные алгоритмы имеют одно преимущество: они не используют дополнительную память, предполагая, что массивы.сортировка использует inplace версии сортировки QuickSort или merge. Но поскольку мы говорим об анаграммах, я предположу, что мы говорим о человеческих языках, поэтому слова не должны быть достаточно длинными, чтобы вызвать проблемы с памятью.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package Algorithms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import javax.swing.JOptionPane;
/**
*
* @author Mokhtar
*/
public class Anagrams {
//Write aprogram to check if two words are anagrams
public static void main(String[] args) {
Anagrams an=new Anagrams();
ArrayList<String> l=new ArrayList<String>();
String result=JOptionPane.showInputDialog("How many words to test anagrams");
if(Integer.parseInt(result) >1)
{
for(int i=0;i<Integer.parseInt(result);i++)
{
String word=JOptionPane.showInputDialog("Enter word #"+i);
l.add(word);
}
System.out.println(an.isanagrams(l));
}
else
{
JOptionPane.showMessageDialog(null, "Can not be tested, \nYou can test two words or more");
}
}
private static String sortString( String w )
{
char[] ch = w.toCharArray();
Arrays.sort(ch);
return new String(ch);
}
public boolean isanagrams(ArrayList<String> l)
{
boolean isanagrams=true;
ArrayList<String> anagrams = null;
HashMap<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
for(int i=0;i<l.size();i++)
{
String word = l.get(i);
String sortedWord = sortString(word);
anagrams = map.get( sortedWord );
if( anagrams == null ) anagrams = new ArrayList<String>();
anagrams.add(word);
map.put(sortedWord, anagrams);
}
for(int h=0;h<l.size();h++)
{
if(!anagrams.contains(l.get(h)))
{
isanagrams=false;
break;
}
}
return isanagrams;
//}
}
}
Я разработчик C++, и приведенный ниже код находится на C++. Я считаю, что самый быстрый и простой способ сделать это будет следующим:
создайте вектор ints размера 26 со всеми слотами, инициализированными до 0, и поместите каждый символ строки в соответствующее положение в векторе. Помните, что вектор находится в алфавитном порядке, и поэтому, если первая буква в строке z, она будет идти в myvector[26]. Примечание: это можно сделать с помощью символов ASCII, так что по существу ваш код будет выглядеть примерно так:
string s = zadg;
for(int i =0; i < s.size(); ++i){
myvector[s[i] - 'a'] = myvector['s[i] - 'a'] + 1;
}
таким образом, вставка всех элементов займет O(n) время, так как вы только пересечете список один раз. Теперь вы можете сделать то же самое для второй строки, и это тоже займет O(n) время. Затем вы можете сравнить два вектора, проверив, совпадают ли счетчики в каждом слоте. Если они есть, это означает, что у вас было одинаковое количество каждого символа в обеих строках, и поэтому они являются анаграммами. Сравнение двух векторов должно также занять O (n) время, поскольку вы только проходите через него один раз.
Примечание: код работает только для одного слова персонажей. Если у вас есть пробелы, цифры и символы, вы можете просто создать вектор размером 96 (ASCII - символы 32-127) и вместо того, чтобы говорить - "a", вы бы сказали -"", поскольку символ пробела является первым в списке символов ASCII.
надеюсь, это поможет. Если я где-то ошибся, пожалуйста, оставьте комментарий.
много сложных ответов здесь. База на принято отвечать и комментарий упоминая проблему " ac " - "bb", предполагая, что A=1 B=2 C=3, мы могли бы просто использовать квадрат каждого целого числа, представляющего символ, и решить проблему:
public boolean anagram(String s, String t) {
if(s.length() != t.length())
return false;
int value = 0;
for(int i = 0; i < s.length(); i++){
value += ((int)s.charAt(i))^2;
value -= ((int)t.charAt(i))^2;
}
return value == 0;
}
Спасибо за указание сделать комментарий, делая комментарий, я обнаружил, что была неправильная логика. Я исправил логику и добавил комментарий для каждого фрагмента кода.
// Time complexity: O(N) where N is number of character in String
// Required space :constant space.
// will work for string that contains ASCII chars
private static boolean isAnagram(String s1, String s2) {
// if length of both string's are not equal then they are not anagram of each other
if(s1.length() != s2.length())return false;
// array to store the presence of a character with number of occurrences.
int []seen = new int[256];
// initialize the array with zero. Do not need to initialize specifically since by default element will initialized by 0.
// Added this is just increase the readability of the code.
Arrays.fill(seen, 0);
// convert each string to lower case if you want to make ABC and aBC as anagram, other wise no need to change the case.
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
// iterate through the first string and count the occurrences of each character
for(int i =0; i < s1.length(); i++){
seen[s1.charAt(i)] = seen[s1.charAt(i)] +1;
}
// iterate through second string and if any char has 0 occurrence then return false, it mean some char in s2 is there that is not present in s1.
// other wise reduce the occurrences by one every time .
for(int i =0; i < s2.length(); i++){
if(seen[s2.charAt(i)] ==0)return false;
seen[s2.charAt(i)] = seen[s2.charAt(i)]-1;
}
// now if both string have same occurrence of each character then the seen array must contains all element as zero. if any one has non zero element return false mean there are
// some character that either does not appear in one of the string or/and mismatch in occurrences
for(int i = 0; i < 256; i++){
if(seen[i] != 0)return false;
}
return true;
}
вот мое решение.Сначала взорвите строки в массивы символов, затем отсортируйте их, а затем сравните, равны они или нет. Я предполагаю, что временная сложность этого кода составляет O (a+b).если a=b, мы можем сказать O (2A)
public boolean isAnagram(String s1, String s2) {
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
if (s1.length() != s2.length())
return false;
char arr1[] = s1.toCharArray();
char arr2[] = s2.toCharArray();
Arrays.sort(arr1);
Arrays.sort(arr2);
for (char c : arr1) {
sb1.append(c);
}
for (char c : arr2) {
sb2.append(c);
}
System.out.println(sb1.toString());
System.out.println(sb2.toString());
if (sb1.toString().equals(sb2.toString()))
return true;
else
return false;
}
аналогичный ответ, возможно, был опубликован на C++, здесь он снова на Java. Обратите внимание, что самым элегантным способом было бы использовать Trie для хранения символов в отсортированном порядке, однако это более сложное решение. Один из способов - использовать hashset для хранения всех сравниваемых слов, а затем сравнивать их по одному. Чтобы сравнить их, сделайте массив символов с индексом, представляющим значение ANCII символов (используя нормализатор начиная с ie. ANCII значение ' a ' равно 97) и значение представление числа встречаемости этого символа. Это будет выполняться в o(n) времени и использовать o (m*z) пространство, где m-размер текущего слова и z-размер для storedWord, для которых мы создаем Char[].
public static boolean makeAnagram(String currentWord, String storedWord){
if(currentWord.length() != storedWord.length()) return false;//words must be same length
Integer[] currentWordChars = new Integer[totalAlphabets];
Integer[] storedWordChars = new Integer[totalAlphabets];
//create a temp Arrays to compare the words
storeWordCharacterInArray(currentWordChars, currentWord);
storeWordCharacterInArray(storedWordChars, storedWord);
for(int i = 0; i < totalAlphabets; i++){
//compare the new word to the current charList to see if anagram is possible
if(currentWordChars[i] != storedWordChars[i]) return false;
}
return true;//and store this word in the HashSet of word in the Heap
}
//for each word store its characters
public static void storeWordCharacterInArray(Integer[] characterList, String word){
char[] charCheck = word.toCharArray();
for(char c: charCheck){
Character cc = c;
int index = cc.charValue()-indexNormalizer;
characterList[index] += 1;
}
}
пока все предлагаемые решения работают с отдельной char
предметы, а не код. Я хотел бы предложить два решения для правильной обработки суррогатные пары также (это символы от U + 10000 до U + 10FFFF, состоящей из двух char
товары).
1) в одну строку O (N logn) решение, которое использует Java 8 CharSequence.codePoints()
стрим:
static boolean areAnagrams(CharSequence a, CharSequence b) {
return Arrays.equals(a.codePoints().sorted().toArray(),
b.codePoints().sorted().toArray());
}
2) менее элегантных O (n) решение (в на самом деле, это будет быстрее только для длинных строк с низкими шансами быть анаграммами):
static boolean areAnagrams(CharSequence a, CharSequence b) {
int len = a.length();
if (len != b.length())
return false;
// collect codepoint occurrences in "a"
Map<Integer, Integer> ocr = new HashMap<>(64);
a.codePoints().forEach(c -> ocr.merge(c, 1, Integer::sum));
// for each codepoint in "b", look for matching occurrence
for (int i = 0, c = 0; i < len; i += Character.charCount(c)) {
int cc = ocr.getOrDefault((c = Character.codePointAt(b, i)), 0);
if (cc == 0)
return false;
ocr.put(c, cc - 1);
}
return true;
}
как математик может думать о проблеме перед написанием любого кода:
- отношение "являются анаграммами" между строками является отношение эквивалентности, поэтому разбивает множество всех строк на классы эквивалентности.
- Предположим, у нас было правило выбрать представитель (шпаргалка) от каждого класса, тогда легко проверить, являются ли два класса одинаковыми, сравнивая их представителей.
- An очевидным представителем для набора строк является"наименьший элемент по лексикографическому порядку", который легко вычислить из любого элемента путем сортировки. Например, представителем класса анаграмм, содержащего "hat", является "aht".
в вашем примере "schoolmaster" и "theclassroom"являются анаграммами, потому что они оба находятся в классе анаграммы с кроваткой "acehlmoorsst".
в псевдокоде:
>>> def crib(word):
... return sorted(word)
...
>>> crib("schoolmaster") == crib("theclassroom")
True
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Check if Anagram by Prime Number Logic
* @author Pallav
*
*/
public class Anagram {
public static void main(String args[]) {
System.out.println(isAnagram(args[0].toUpperCase(),
args[1].toUpperCase()));
}
/**
*
* @param word : The String 1
* @param anagram_word : The String 2 with which Anagram to be verified
* @return true or false based on Anagram
*/
public static Boolean isAnagram(String word, String anagram_word) {
//If length is different return false
if (word.length() != anagram_word.length()) {
return false;
}
char[] words_char = word.toCharArray();//Get the Char Array of First String
char[] anagram_word_char = anagram_word.toCharArray();//Get the Char Array of Second String
int words_char_num = 1;//Initialize Multiplication Factor to 1
int anagram_word_num = 1;//Initialize Multiplication Factor to 1 for String 2
Map<Character, Integer> wordPrimeMap = wordPrimeMap();//Get the Prime numbers Mapped to each alphabets in English
for (int i = 0; i < words_char.length; i++) {
words_char_num *= wordPrimeMap.get(words_char[i]);//get Multiplication value for String 1
}
for (int i = 0; i < anagram_word_char.length; i++) {
anagram_word_num *= wordPrimeMap.get(anagram_word_char[i]);//get Multiplication value for String 2
}
return anagram_word_num == words_char_num;
}
/**
* Get the Prime numbers Mapped to each alphabets in English
* @return
*/
public static Map<Character, Integer> wordPrimeMap() {
List<Integer> primes = primes(26);
int k = 65;
Map<Character, Integer> map = new TreeMap<Character, Integer>();
for (int i = 0; i < primes.size(); i++) {
Character character = (char) k;
map.put(character, primes.get(i));
k++;
}
// System.out.println(map);
return map;
}
/**
* get first N prime Numbers where Number is greater than 2
* @param N : Number of Prime Numbers
* @return
*/
public static List<Integer> primes(Integer N) {
List<Integer> primes = new ArrayList<Integer>();
primes.add(2);
primes.add(3);
int n = 5;
int k = 0;
do {
boolean is_prime = true;
for (int i = 2; i <= Math.sqrt(n); i++) {
if (n % i == 0) {
is_prime = false;
break;
}
}
if (is_prime == true) {
primes.add(n);
}
n++;
// System.out.println(k);
} while (primes.size() < N);
// }
return primes;
}
}
IMHO, наиболее эффективное решение было предоставлено @Siguza, я расширил его, чтобы покрыть строки пробелом e.г: "Уильям Шекспир", "я слаб в правописании", "школьный учитель", "классная комната"
public int getAnagramScore(String word, String anagram) {
if (word == null || anagram == null) {
throw new NullPointerException("Both, word and anagram, must be non-null");
}
char[] wordArray = word.trim().toLowerCase().toCharArray();
char[] anagramArray = anagram.trim().toLowerCase().toCharArray();
int[] alphabetCountArray = new int[26];
int reference = 'a';
for (int i = 0; i < wordArray.length; i++) {
if (!Character.isWhitespace(wordArray[i])) {
alphabetCountArray[wordArray[i] - reference]++;
}
}
for (int i = 0; i < anagramArray.length; i++) {
if (!Character.isWhitespace(anagramArray[i])) {
alphabetCountArray[anagramArray[i] - reference]--;
}
}
for (int i = 0; i < 26; i++)
if (alphabetCountArray[i] != 0)
return 0;
return word.length();
}
другое решение без сортировки.
public static boolean isAnagram(String s1, String s2){
//case insensitive anagram
StringBuffer sb = new StringBuffer(s2.toLowerCase());
for (char c: s1.toLowerCase().toCharArray()){
if (Character.isLetter(c)){
int index = sb.indexOf(String.valueOf(c));
if (index == -1){
//char does not exist in other s2
return false;
}
sb.deleteCharAt(index);
}
}
for (char c: sb.toString().toCharArray()){
//only allow whitespace as left overs
if (!Character.isWhitespace(c)){
return false;
}
}
return true;
}
сортировка подход не самый лучший. Он занимает O(n) пространство и O (nlogn) время. Вместо этого создайте хэш-карту символов и подсчитайте их (символы приращения, которые появляются в первой строке, и символы уменьшения, которые появляются во второй строке). Когда некоторое количество достигнет нуля, удалите его из хэша. Наконец, если две строки являются анаграммами, то хэш - таблица будет пустой в конце-иначе она не будет пустой.
пара важных заметок: (1) игнорировать случай письма и (2) Игнорируйте пробел.
вот подробный анализ и реализация в C#:тестирование, если две строки являются анаграммами
простой метод, чтобы выяснить, является ли testString анаграммой baseString.
private static boolean isAnagram(String baseString, String testString){
//Assume that there are no empty spaces in either string.
if(baseString.length() != testString.length()){
System.out.println("The 2 given words cannot be anagram since their lengths are different");
return false;
}
else{
if(baseString.length() == testString.length()){
if(baseString.equalsIgnoreCase(testString)){
System.out.println("The 2 given words are anagram since they are identical.");
return true;
}
else{
List<Character> list = new ArrayList<>();
for(Character ch : baseString.toLowerCase().toCharArray()){
list.add(ch);
}
System.out.println("List is : "+ list);
for(Character ch : testString.toLowerCase().toCharArray()){
if(list.contains(ch)){
list.remove(ch);
}
}
if(list.isEmpty()){
System.out.println("The 2 words are anagrams");
return true;
}
}
}
}
return false;
}
извините, решение находится в C#, но я думаю, что различные элементы, используемые для получения решения, довольно интуитивны. Небольшая настройка требуется для дефисных слов, но для обычных слов она должна работать нормально.
internal bool isAnagram(string input1,string input2)
{
Dictionary<char, int> outChars = AddToDict(input2.ToLower().Replace(" ", ""));
input1 = input1.ToLower().Replace(" ","");
foreach(char c in input1)
{
if (outChars.ContainsKey(c))
{
if (outChars[c] > 1)
outChars[c] -= 1;
else
outChars.Remove(c);
}
}
return outChars.Count == 0;
}
private Dictionary<char, int> AddToDict(string input)
{
Dictionary<char, int> inputChars = new Dictionary<char, int>();
foreach(char c in input)
{
if(inputChars.ContainsKey(c))
{
inputChars[c] += 1;
}
else
{
inputChars.Add(c, 1);
}
}
return inputChars;
}
Я видел, что никто не использовал подход "хэш-кода", чтобы узнать анаграммы. Я обнаружил, что мой подход мало отличается от подходов, рассмотренных выше, поэтому подумал о том, чтобы поделиться им. Я написал приведенный ниже код, чтобы найти анаграммы, которые работают в O (n).
/**
* This class performs the logic of finding anagrams
* @author ripudam
*
*/
public class AnagramTest {
public static boolean isAnagram(final String word1, final String word2) {
if (word1 == null || word2 == null || word1.length() != word2.length()) {
return false;
}
if (word1.equals(word2)) {
return true;
}
final AnagramWrapper word1Obj = new AnagramWrapper(word1);
final AnagramWrapper word2Obj = new AnagramWrapper(word2);
if (word1Obj.equals(word2Obj)) {
return true;
}
return false;
}
/*
* Inner class to wrap the string received for anagram check to find the
* hash
*/
static class AnagramWrapper {
String word;
public AnagramWrapper(final String word) {
this.word = word;
}
@Override
public boolean equals(final Object obj) {
return hashCode() == obj.hashCode();
}
@Override
public int hashCode() {
final char[] array = word.toCharArray();
int hashcode = 0;
for (final char c : array) {
hashcode = hashcode + (c * c);
}
return hashcode;
}
}
}
Я знаю, это старый вопрос. Тем не менее, я надеюсь, что это может кому-то помочь. Временная сложность этого решения равна O (n^2).
public boolean areAnagrams(final String word1, final String word2) {
if (word1.length() != word2.length())
return false;
if (word1.equals(word2))
return true;
if (word1.length() == 0 && word2.length() == 0)
return true;
String secondWord = word2;
for (int i = 0; i < word1.length(); i++) {
if (secondWord.indexOf(word1.charAt(i)) == -1)
return false;
secondWord = secondWord.replaceFirst(word1.charAt(i) + "", "");
}
if (secondWord.length() > 0)
return false;
return true;
}
вот еще один подход с использованием HashMap в Java
public static boolean isAnagram(String first, String second) {
if (first == null || second == null) {
return false;
}
if (first.length() != second.length()) {
return false;
}
return doCheckAnagramUsingHashMap(first.toLowerCase(), second.toLowerCase());
}
private static boolean doCheckAnagramUsingHashMap(final String first, final String second) {
Map<Character, Integer> counter = populateMap(first, second);
return validateMap(counter);
}
private static boolean validateMap(Map<Character, Integer> counter) {
for (int val : counter.values()) {
if (val != 0) {
return false;
}
}
return true;
}
вот тестовый пример
@Test
public void anagramTest() {
assertTrue(StringUtil.isAnagram("keep" , "PeeK"));
assertFalse(StringUtil.isAnagram("Hello", "hell"));
assertTrue(StringUtil.isAnagram("SiLeNt caT", "LisTen cat"));
}
private static boolean checkAnagram(String s1, String s2) {
if (s1 == null || s2 == null) {
return false;
} else if (s1.length() != s2.length()) {
return false;
}
char[] a1 = s1.toCharArray();
char[] a2 = s2.toCharArray();
int length = s2.length();
int s1Count = 0;
int s2Count = 0;
for (int i = 0; i < length; i++) {
s1Count+=a1[i];
s2Count+=a2[i];
}
return s2Count == s1Count ? true : false;
}
вы должны использовать что-то вроде этого:
for (int i...) {
isFound = false;
for (int j...) {
if (...) {
...
isFound = true;
}
}
значение по умолчанию isFound
должно быть false. Просто это
способ решить эту проблему-на основе ответа Саи Кирана..
import java.util.Scanner;
public class Anagram {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("Enter first word : ");
String word1 = sc.nextLine();
System.out.print("Enter second word : ");
String word2 = sc.nextLine();
sc.close();
System.out.println("Is Anagram : " + isAnagram(word1, word2));
}
private static boolean isAnagram(String word1, String word2) {
if (word1.length() != word2.length()) {
System.err.println("Words length didn't match!");
return false;
}
char ch1, ch2;
int len = word1.length(), sumOfWord1Chars = 0, sumOfWord2Chars = 0;
for (int i = 0; i < len; i++) {
ch1 = word1.charAt(i);
if (word2.indexOf(ch1) < 0) {
System.err.println("'" + ch1 + "' not found in \"" + word2
+ "\"");
return false;
}
sumOfWord1Chars += word1.charAt(i);
ch2 = word2.charAt(i);
if (word1.indexOf(ch2) < 0) {
System.err.println("'" + ch2 + "' not found in \"" + word1
+ "\"");
return false;
}
sumOfWord2Chars += word2.charAt(i);
}
if (sumOfWord1Chars != sumOfWord2Chars) {
System.err
.println("Sum of both words didn't match, i.e., words having same characters but with different counts!");
return false;
}
return true;
}
}
работает отлично! Но не хороший подход, потому что он работает в O(n^2)
boolean isAnagram(String A, String B) {
if(A.length() != B.length())
return false;
A = A.toLowerCase();
B = B.toLowerCase();
for(int i = 0; i < A.length(); i++){
boolean found = false;
for(int j = 0; j < B.length(); j++){
if(A.charAt(i) == B.charAt(j)){
found = true;
break;
}
}
if(!found){
return false;
}
}
for(int i = 0; i < B.length(); i++){
boolean found = false;
for(int j = 0; j < A.length(); j++){
if(A.charAt(j) == B.charAt(i)){
found = true;
break;
}
}
if(!found){
return false;
}
}
int sum1 = 0, sum2 = 0;
for(int i = 0; i < A.length(); i++){
sum1 += (int)A.charAt(i);
sum2 += (int)B.charAt(i);
}
if(sum1 == sum2){
return true;
}
return false;
}
Я написал эту программу на java. Я думаю, что это также может помочь:
public class Anagram {
public static void main(String[] args) {
checkAnagram("listen", "silent");
}
public static void checkAnagram(String str1, String str2) {
boolean isAnagram = false;
str1 = sortStr(str1);
str2 = sortStr(str2);
if (str1.equals(str2)) {
isAnagram = true;
}
if (isAnagram) {
System.out.println("Two strings are anagram");
} else {
System.out.println("Two string are not anagram");
}
}
public static String sortStr(String str) {
char[] strArr = str.toCharArray();
for (int i = 0; i < str.length(); i++) {
for (int j = i + 1; j < str.length(); j++) {
if (strArr[i] > strArr[j]) {
char temp = strArr[i];
strArr[i] = strArr[j];
strArr[j] = temp;
}
}
}
String output = String.valueOf(strArr);
return output;
}
}