次のコードをJava Netbeansで記述しています。これは通常のアナグラムで非常にうまく機能します。ただし、2つのテキストフィールドに繰り返し文字を含む単語が含まれている場合、コードは機能しません。問題とそれをどのように解決できますか?Javaの基本であり、まだ配列を理解できません。
String s1= t1.getText();
String s2= t2.getText();
int b=0,c=0;
if(s1.length()!=s2.length())
System.out.print("No");
else {
for(int i=0;i<s1.length();i++) {
char s = s1.charAt(i);
for(int j=0;j<s2.length();j++) {
if(s==s2.charAt(j)){
b++;
}
}
if(b==0)
break;
}
if(b==0)
System.out.print("No");
else
System.out.print("YES");
}
System.out.print(b);
私は、より簡単な理由を考えます。2つの文字列は、いったん並べ替えると、それらが完全に一致する場合、アナグラムです。したがって、Javaのようになります。
String s1 = "cat";
String s2 = "tac";
boolean isAnagram = false;
if (s1.length() == s2.length()) {
char[] s1AsChar = s1.toCharArray();
char[] s2AsChar = s2.toCharArray();
Arrays.sort(s1AsChar);
Arrays.sort(s2AsChar);
isAnagram = Arrays.equals(s1AsChar, s2AsChar);
}
ここで私の解決策は、最初の文字列の各文字の外観を数え、2番目の文字列の数からそれを差し引くことです。最後に、文字数が0でないかどうかを確認します。2つの文字列がアナグラムではありません。
public static boolean isAnagram(String a, String b){
//assume that we are using ASCII
int[] charCnt = new int[256];
for(int i = 0; i < a.length(); i++){
charCnt[a.charAt(i)]++;
}
for(int i = 0; i< b.length(); i++){
charCnt[b.charAt(i)]--;
}
for(int i = 0; i<charCnt.length; i++){
if(charCnt[i] != 0) return false;
}
return true;
}
ソートされた文字を比較したい。それはワンライナーです:
_return Arrays.equals(s1.chars().sorted().toArray(),
s2.chars().sorted().toArray());
_
Arrays.equals()
は、長さとすべての要素を比較します。
あなたは初心者のように見えるので、他のクラスやストリームの関数を含まない解決策があります。これは、配列の使用とchar
がint
を表すこともできるという事実のみを含みます。
public static void main(String[] args) throws ParseException {
String s1= "anagram";
String s2= "margana";
// We make use of the fact that a char does also represent an int.
int lettersS1[] = new int[Character.MAX_VALUE];
int lettersS2[] = new int[Character.MAX_VALUE];
if(s1.length()!=s2.length())
System.out.print("No");
else {
// Loop through the String once
for(int i = 0; i<s1.length() ;++i) {
// we can just use the char value as an index
// and increase the value of it. This is our identifier how often
// each letter was aviable in the String. Alse case insensitive right now
lettersS1[s1.toLowerCase().charAt(i)]++;
lettersS2[s2.toLowerCase().charAt(i)]++;
}
// set a flag if the Strings were anagrams
boolean anag = true;
// We stop the loop as soon as we noticed they are not anagrams
for(int i = 0;i<lettersS1.length&&anag;++i) {
if(lettersS1[i] != lettersS2[i]) {
// If the values differ they are not anagrams.
anag = false;
}
}
// Depending on the former loop we know if these two strings are anagrams
if(anag) {
System.out.print("Anagram");
} else {
System.out.print("No anagram");
}
}
}
発生カウンターに基づくもう1つの解決策:
static boolean areAnagrams(CharSequence a, CharSequence b) {
int len = a.length();
if (len != b.length())
return false;
// collect char occurrences in "a"
Map<Character, Integer> occurrences = new HashMap<>(64);
for (int i = 0; i < len; i++)
occurrences.merge(a.charAt(i), 1, Integer::sum);
// for each char in "b", look for matching occurrence
for (int i = 0; i < len; i++) {
char c = b.charAt(i);
int cc = occurrences.getOrDefault(c, 0);
if (cc == 0)
return false;
occurrences.put(c, cc - 1);
}
return true;
}
このソリューションは「並べ替えと比較」よりもエレガントではありませんが、O(n)で動作するため、アナグラムになる可能性が少ない長い文字列に対してはより効果的です。 O(n logn)の代わりに、一致するオカレンスが2番目の文字列のある位置で見つからない場合はすぐに戻ります。
「Basic Java」テリトリーから抜け出し、アルゴリズムを変更して サロゲートペア も処理できるようにしました。ここで収集および照合されるのはchar
sではなく、int
コードポイントです。
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;
}