2つの単語が互いにアナグラムであるかどうかを示すプログラムがあります。適切に動作しない例がいくつかありますが、助けていただければ幸いです。1年目のプログラマーであるので、それが進歩していなければ素晴らしいことです。 「schoolmaster」と「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;
}
}
最速のアルゴリズムは、26の英語文字のそれぞれを一意の素数にマッピングすることです。次に、文字列の積を計算します。算術の基本定理により、2つの文字列は、それらの積が同じである場合にのみアナグラムです。
同じ数の文字と同じ文字が含まれている場合、2つの単語は互いにアナグラムです。文字を辞書式順序で並べ替えるだけで、1つの文字列のすべての文字がに等しく、と同じ順序であるかどうかを判断する必要があります。他の文字列の文字。
以下にコード例を示します。 APIで Arrays
を調べて、ここで何が起こっているのかを理解してください。
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)になります。ただし、ハッシュマップを使用する場合、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)ソリューションを次に示します。最初の配列の各文字のカウントをインクリメントし、2番目の配列の各文字のカウントをデクリメントします。結果の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)
になります。
基数ソートを使用すると、O(N)
スペースを含むO(M)
になります。ここで、M
はアルファベットの個別の文字数です。 (これは英語では26ですが...理論的には多言語アナグラムを考慮すべきです。)
カウントの配列を使用する「文字のカウント」もO(N)
...であり、ソートされた文字列を再構築する必要がないため、基数ソートよりも高速です。スペース使用量はO(M)
になります。
辞書、ハッシュマップ、ツリーマップ、または同等のものを使用した「文字のカウント」は、アルファベットが大きくない限り、配列アプローチよりも遅くなります。
残念なことに、エレガントな「積の積」アプローチは、最悪の場合O(N^2)
です。これは、長すぎる単語やフレーズでは、素数の積がlong
に収まらないためです。つまり、BigInteger
を使用する必要があり、BigInteger
に小さな定数を掛けるN回はO(N^2)
です。
仮想アルファベットの場合、スケーリング係数は大きくなります。素数の積をBigInteger
として保持する最悪の場合のスペース使用量は(と思う)O(N*logM)
です。
単語がアナグラムでない場合、hashcode
ベースのアプローチは通常O(N)
です。ハッシュコードが等しい場合、適切なアナグラムテストを行う必要があります。したがって、これは完全なソリューションではありません。
O(n)ソリューションは、並べ替えを一切行わず、1つのマップのみを使用します。
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;
}
2つの等しい長さの文字列を歩いて、それらの違いを追跡しています。違いが何であるかは気にしません。同じ文字を持っているかどうかを知りたいだけです。これは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英語文字セットに依存していますが、他の言語に変更することは難しくありません。常にMap [Character、Int]を使用して同じ情報を追跡できますが、速度は遅くなります。
ここで多くの複雑な答え。 受け入れられた回答 と 「ac」に言及したコメント に基づいて- A = 65 B = 66 C = 67を想定した「bb」の問題では、charを表す各整数の2乗を使用して問題を解決できます。
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;
}
/*
* 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;
//}
}
}
より多くのメモリ(最大N/2要素のHashMap)を使用することにより、文字列を並べ替える必要がなくなります。
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個の配列(アクセントや装飾なしのaからzまで)の配列のみを使用できます。
定義する場合:N = | one | + | two | Nに対して1回の反復を行います(1を1回超えるとカウンターをインクリメントし、1回を2ずつデクリメントする)。次に、合計を確認するために、mose N/2で繰り返します。
説明した他のアルゴリズムには1つの利点があります。Arrays.sortがインプレースバージョンのQuickSortまたはマージソートを使用すると仮定すると、余分なメモリを使用しません。しかし、アナグラムについて話しているので、私たちは人間の言語について話していると思います。したがって、言葉は記憶の問題を与えるのに十分長くないはずです。
私はC++開発者であり、以下のコードはC++です。最も速くて簡単な方法は次のとおりです。
サイズ26のintのベクトルを作成し、すべてのスロットを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;
}
したがって、すべての要素を挿入するには、リストを1回だけ走査するので、O(n)時間かかります。これで、2番目の文字列に対してまったく同じことを行うことができ、それもO(n)時間かかります。次に、各スロットのカウンタが同じかどうかを確認して、2つのベクトルを比較できます。もしそうなら、それはあなたが両方の文字列に同じ数の各文字を持っていることを意味し、したがってそれらはアナグラムです。また、2つのベクトルの比較にはO(n)時間かかる必要があります。これは、1回だけ通過するためです。
注:コードは、単一のWordの文字に対してのみ機能します。スペースと数字と記号がある場合は、サイズ96(ASCII文字32-127)のベクトルを作成するだけで、スペース文字がASCII文字のリスト。
それがお役に立てば幸いです。どこかに間違いを犯した場合は、コメントを残してください。
コメントをすることを指摘してくれてありがとう、コメントをしながら、間違ったロジックがあることがわかりました。ロジックを修正し、各コードにコメントを追加しました。
// 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;
}
私見、最も効率的な解決策は@Siguzaによって提供されました、私はそれをスペースで文字列をカバーするように拡張しました。
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();
}
これまでに提案されたすべてのソリューションは、コードポイントではなく、個別のchar
アイテムで動作します。 サロゲートペア (文字 + 10000からU + 10FFFF 、2つのchar
で構成される)を適切に処理する2つのソリューションを提案したいと思います。アイテム)。
1)Java 8 CharSequence.codePoints()
ストリームを利用する1行O(n logn)ソリューション:
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;
}
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;
}
}
数学者がコードを書く前に問題をどのように考えるか:
あなたの例では、「schoolmaster」と「theclassroom」はアナグラムです。これらは両方ともベビーベッド「acehlmoorsst」のアナグラムクラスに属しているためです。
擬似コードで:
>>> def crib(Word):
... return sorted(Word)
...
>>> crib("schoolmaster") == crib("theclassroom")
True
私の解決策は次のとおりです。最初に文字列をchar配列に分解し、次にそれらをソートしてから、等しいかどうかを比較します。このコードの時間の複雑さは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を使用して文字をソートされた順序で保存することですが、それはより複雑なソリューションです。 1つの方法は、ハッシュセットを使用して、比較するすべての単語を保存し、それらを1つずつ比較することです。それらを比較するには、文字のANCII値を表すインデックス(「a」のANCII値が97であるためノーマライザーを使用)とその文字の出現回数を表す値を持つ文字の配列を作成します。これはO(n)時間で実行され、O(m * z)スペースを使用します。ここで、mはcurrentWordのサイズで、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;
}
}
並べ替えの方法は最適ではありません。 O(n)スペースとO(nlogn)時間かかります。代わりに、文字のハッシュマップを作成してカウントします(最初の文字列に表示される文字を増やし、2番目の文字列に表示される文字を減らします)。カウントがゼロになったら、ハッシュから削除します。最後に、2つの文字列がアナグラムである場合、ハッシュテーブルは最後に空になります。そうでない場合は空になりません。
重要な注意事項:(1)大文字小文字を無視し、(2)空白を無視します。
C#での詳細な分析と実装は次のとおりです。 2つの文字列がアナグラムであるかどうかのテスト
JavaでHashMapを使用する別のアプローチを次に示します。
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"));
}
// When this method returns 0 means strings are Anagram, else Not.
public static int isAnagram(String str1, String str2) {
int value = 0;
if (str1.length() == str2.length()) {
for (int i = 0; i < str1.length(); i++) {
value = value + str1.charAt(i);
value = value - str2.charAt(i);
}
} else {
value = -1;
}
return value;
}
複雑さO(N)を持つ最も単純なソリューションは、Mapを使用することです。
public static Boolean checkAnagram(String string1, String string2) {
Boolean anagram = true;
Map<Character, Integer> map1 = new HashMap<>();
Map<Character, Integer> map2 = new HashMap<>();
char[] chars1 = string1.toCharArray();
char[] chars2 = string2.toCharArray();
for(int i=0; i<chars1.length; i++) {
if(map1.get(chars1[i]) == null) {
map1.put(chars1[i], 1);
} else {
map1.put(chars1[i], map1.get(chars1[i])+1);
}
if(map2.get(chars2[i]) == null) {
map2.put(chars2[i], 1);
} else {
map2.put(chars2[i], map2.get(chars2[i])+1);
}
}
Set<Map.Entry<Character, Integer>> entrySet1 = map1.entrySet();
Set<Map.Entry<Character, Integer>> entrySet2 = map2.entrySet();
for(Map.Entry<Character, Integer> entry:entrySet1) {
if(entry.getValue() != map2.get(entry.getKey())) {
anagram = false;
break;
}
}
return anagram;
}
アナグラムを見つけるために「ハッシュコード」アプローチを使用した人はいないことがわかりました。私のアプローチは、上記で説明したアプローチとほとんど変わらないため、共有することを考えました。 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;
}
}
}
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;
}
ソートなしのその他のソリューション。
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;
}
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;
}