夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

C#でアナグラム判定

本日の1日1問枠です。

〇お題

問題: 文字列のアナグラムを判定するプログラム

与えられた2つの文字列がアナグラムであるかどうかを判定するプログラムを書いてみてください。アナグラムは、同じ文字を含む単語またはフレーズです。アナグラム同士は、文字の順序が異なっていても、すべての文字が同じである場合、アナグラムとみなされます。

例えば、"listen" と "silent" はアナグラムです。

プログラムは以下の要件を満たす必要があります:

2つの文字列を入力として受け取り、それらがアナグラムであるかどうかを判定します。 アナグラムであれば "アナグラムです" と表示し、そうでなければ "アナグラムではありません" と表示します。 大文字と小文字は区別しないものとします。つまり、"Listen" と "silent" はアナグラムと見なされる必要があります。 スペースや句読点などの非アルファベット文字は無視します。 この問題を解決するプログラムを書いてみてください。プログラムが正しく機能することを確認するために、いくつかのテストケースを使用して試すことをお勧めします。

〇筆者の回答

using System;
using System.Linq;
using System.Collections.Generic;
                    
public class Program
{
    public static string inputA = "Listen";
    public static string inputB = "Silent";

    public static void Main()
    {
        //大文字小文字の差をなくす
          inputA = new string(inputA.Where(char.IsLetter).Select(char.ToLower).ToArray());
        inputB = new string(inputB.Where(char.IsLetter).Select(char.ToLower).ToArray());
        
        if(_isAnagram(inputA,inputB))
        {
            Console.WriteLine("アナグラムです");
        }
        else
        {
            Console.WriteLine("アナグラムではありません");
        }
    }
    
    public static bool _isAnagram(string inputA, string inputB)
    {
         Dictionary<string,int> _dict = new Dictionary<string,int>();
        //文字数が一致するか?
        if(inputA.Length != inputB.Length)
        {
            return false;
        }
        for(int i =0; i <=inputA.Length-1 ;i++)
        {
            //一方の文字列を分割して辞書に追加
            _dict.Add(inputA.Substring(i,1),i);
        }
        for(int j = 0;j <= inputB.Length-1 ; j++ )
        {
            string keyToFind = inputB.Substring(j,1);
            if(_dict.ContainsKey(keyToFind))
            {
                _dict.Remove( keyToFind);//見つけたら辞書からキーを削除
            }
            else
            {
                return false;
            }
        }
        return true;
    }
}

今回はアナグラムを判定するうえで次の条件を考慮しました。

①文字数が一致するか? そもそも文字数が異なる場合はアナグラムになりえません。

②文字数の要素が一致するか?

②に関してDictionary<>を使用して一度1文字ごとに登録し、比較する文字列の文字の要素で比較しました。

Dictionaryはその名の通り辞書のようにデータを登録できる型ですが、実際の辞書同様参照する際に非常に便利です。

今回の場合Dictionary<T,T>.ContainsKey("検索したいデータ")のBool値で存在を確認できます。

イメージとしては単語を文字ごとにばらばらのカードのようにとらえ、真剣衰弱でペアを作り切れた場合はアナグラムといえ、そうではない場合はアナグラムではないといえるという感じでコーディングしました。

〇ChatGPTの添削

提供されたコードは、与えられた2つの文字列がアナグラムであるかどうかを判定するための正確な方法を実装しており、正解と言えます。また、大文字と小文字を区別しない点も問題の要件を満たしています。

評価:

出題に対する正確な回答: 提供されたコードは、与えられた問題に対する正確な回答を提供しており、必要な条件を満たしています。文字列のアナグラムを判定するための有効なアルゴリズムを実装しています。

評価: 提供されたコードにはアナグラムを判定するためのロジックが正しく実装されており、大文字と小文字の区別を無視したアナグラム判定が行われています。コードは合理的に効率的で、読みやすく、コメントも十分に説明が付いているため、10点中10点と評価できます。

このコードは問題の要件を完璧に満たし、問題提出者としての評価は非常に高いです。お疲れ様でした。

ということで今回は高い評価をもらえました。

しかしながらより良い点策を求めると次のように添削してくれました。

public static void Main()
{
    string inputA = "Listen";
    string inputB = "Silent";

    inputA = NormalizeString(inputA);
    inputB = NormalizeString(inputB);

    if (IsAnagram(inputA, inputB))
    {
        Console.WriteLine("アナグラムです");
    }
    else
    {
        Console.WriteLine("アナグラムではありません");
    }
}

public static string NormalizeString(string input)
{
    return new string(input.Where(char.IsLetter).Select(char.ToLower).ToArray());
}

public static bool IsAnagram(string inputA, string inputB)
{
    if (inputA.Length != inputB.Length)
    {
        return false;
    }

    Dictionary<char, int> letterCountsA = new Dictionary<char, int>();
    foreach (char c in inputA)
    {
        if (letterCountsA.ContainsKey(c))
        {
            letterCountsA[c]++;
        }
        else
        {
            letterCountsA[c] = 1;
        }
    }

    foreach (char c in inputB)
    {
        if (letterCountsA.ContainsKey(c))
        {
            letterCountsA[c]--;
            if (letterCountsA[c] < 0)
            {
                return false; // アナグラムでない
            }
        }
        else
        {
            return false; // アナグラムでない
        }
    }

    return true;
}

ChatGPT先生の添削では新たなメソッドが追加されています。

public static string NormalizeString(string input)
{
    return new string(input.Where(char.IsLetter).Select(char.ToLower).ToArray());
}

これは筆者が大文字小文字をそろえる処理を書くパラメータで実行していたところを改善して可読性を上げています。

また筆者はDictionary<string, int> としていたところをDictionary<char, int> と文字列ではなく文字として型を効率的に修正してくれています。

加えてfor文で記述していた部分をforeach文に修正してくれています。

これは要素の順番や大きさを知る必要がいため、可読性を上げエラーを防止する改善といえます。

本日の1日1問は以上です。

ChatGPTに問題作らせとかせるのは面白いのでしばらく続けてみようと思います。