Java で文字列が回文かどうかをチェックする方法

JavaJavaBeginner
今すぐ練習

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

はじめに

この実験では、Java で文字列が回文 (palindrome) かどうかをチェックする方法を学びます。まずは、文字列を反転させて元の文字列と比較する基本的な方法から始め、さまざまなアプローチを探っていきます。

これを基に、次には二つのポインタ (two-pointer) アプローチを使ったより効率的な手法を学びます。最後に、大文字小文字や非文字の文字を無視して回文チェックを行うように機能を拡張し、チェックをより堅牢なものにします。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("Java")) -.-> java/BasicSyntaxGroup(["Basic Syntax"]) java(("Java")) -.-> java/StringManipulationGroup(["String Manipulation"]) java(("Java")) -.-> java/SystemandDataProcessingGroup(["System and Data Processing"]) java/BasicSyntaxGroup -.-> java/operators("Operators") java/BasicSyntaxGroup -.-> java/for_loop("For Loop") java/BasicSyntaxGroup -.-> java/while_loop("While Loop") java/StringManipulationGroup -.-> java/strings("Strings") java/SystemandDataProcessingGroup -.-> java/string_methods("String Methods") subgraph Lab Skills java/operators -.-> lab-559990{{"Java で文字列が回文かどうかをチェックする方法"}} java/for_loop -.-> lab-559990{{"Java で文字列が回文かどうかをチェックする方法"}} java/while_loop -.-> lab-559990{{"Java で文字列が回文かどうかをチェックする方法"}} java/strings -.-> lab-559990{{"Java で文字列が回文かどうかをチェックする方法"}} java/string_methods -.-> lab-559990{{"Java で文字列が回文かどうかをチェックする方法"}} end

文字列を反転させて比較する

このステップでは、文字列を反転させ、元の文字列と比較する単純な Java プログラムを作成します。これは、Java での文字列操作を理解するための基本的なステップであり、文字列が回文 (palindrome) かどうかをチェックするための土台となります。

まず、新しい Java ファイルを作成しましょう。WebIDE の左側にあるファイルエクスプローラーで、~/project ディレクトリ内を右クリックし、「新しいファイル」を選択して、StringReverse.java と名付けます。

次に、エディターで StringReverse.java ファイルを開き、以下のコードを追加します。

public class StringReverse {

    public static void main(String[] args) {
        String originalString = "hello";
        String reversedString = "";

        // Loop through the original string from end to beginning
        for (int i = originalString.length() - 1; i >= 0; i--) {
            // Append each character to the reversedString
            reversedString += originalString.charAt(i);
        }

        System.out.println("Original string: " + originalString);
        System.out.println("Reversed string: " + reversedString);

        // Compare the original and reversed strings
        if (originalString.equals(reversedString)) {
            System.out.println("The string is the same forwards and backwards.");
        } else {
            System.out.println("The string is NOT the same forwards and backwards.");
        }
    }
}

このコードを分解してみましょう。

  • String originalString = "hello";: originalString という名前の String 型の変数を宣言し、"hello" という値を代入します。
  • String reversedString = "";: reversedString という名前の空の String 型の変数を宣言します。ここに反転した文字列を構築します。
  • for (int i = originalString.length() - 1; i >= 0; i--): これは for ループで、originalString の最後の文字 (インデックス length() - 1) から最初の文字 (インデックス 0) まで逆順に処理します。
  • reversedString += originalString.charAt(i);: ループ内で、originalString.charAt(i) は現在のインデックス i の文字を取得します。そして、この文字を reversedString に追加します。
  • System.out.println(...): これらの行は、元の文字列と反転した文字列をコンソールに出力します。
  • if (originalString.equals(reversedString)): この if 文は、equals() メソッドを使用して originalStringreversedString を比較します。Java では、文字列の内容を比較するには equals() を使用し、== 演算子を使用しないでください。
  • if ブロックと else ブロックは、文字列が同じかどうかに応じて異なるメッセージを出力します。

StringReverse.java ファイルを保存します (Ctrl+S または Cmd+S)。

次に、WebIDE の下部にあるターミナルを開きます。~/project ディレクトリにいることを確認してください。もしそうでない場合は、cd ~/project と入力して Enter キーを押します。

javac コマンドを使用して Java コードをコンパイルします。

javac StringReverse.java

エラーがなければ、~/project ディレクトリに StringReverse.class ファイルが作成されます。

最後に、java コマンドを使用してコンパイルされた Java プログラムを実行します。

java StringReverse

以下のような出力が表示されるはずです。

Original string: hello
Reversed string: olleh
The string is NOT the same forwards and backwards.

この場合、"hello" は前後が同じではないため、プログラムは正しくそれを識別しています。

二つのポインタ (Two-Pointer) アプローチを使った回文チェック

前のステップでは、文字列を完全に反転させて回文 (palindrome) かどうかをチェックしました。この方法は機能しますが、新しい文字列を作成する必要があり、非常に長い文字列に対しては非効率的です。より効率的なアプローチは、二つのポインタ (two-pointer) テクニック を使用することです。

二つのポインタテクニックでは、文字列の両端から始まり、中央に向かって移動する二つのポインタ (インデックス位置を保持する変数) を使用します。これらのポインタが指す文字を比較します。ポインタが内側に移動する間、文字が常に同じであれば、その文字列は回文です。不一致が見つかれば、回文ではありません。

これを実装する新しい Java プログラムを作成しましょう。左側のファイルエクスプローラーで、~/project ディレクトリに PalindromeChecker.java という名前の新しいファイルを作成します。

PalindromeChecker.java を開き、以下のコードを追加します。

public class PalindromeChecker {

    public static void main(String[] args) {
        String testString = "madam"; // Let's test with a palindrome
        boolean isPalindrome = checkPalindrome(testString);

        if (isPalindrome) {
            System.out.println("\"" + testString + "\" is a palindrome.");
        } else {
            System.out.println("\"" + testString + "\" is not a palindrome.");
        }

        testString = "racecar"; // Another palindrome
        isPalindrome = checkPalindrome(testString);

        if (isPalindrome) {
            System.out.println("\"" + testString + "\" is a palindrome.");
        } else {
            System.out.println("\"" + testString + "\" is not a palindrome.");
        }

        testString = "hello"; // Not a palindrome
        isPalindrome = checkPalindrome(testString);

        if (isPalindrome) {
            System.out.println("\"" + testString + "\" is a palindrome.");
        } else {
            System.out.println("\"" + testString + "\" is not a palindrome.");
        }
    }

    // Method to check if a string is a palindrome using two pointers
    public static boolean checkPalindrome(String str) {
        // Initialize two pointers
        int left = 0; // Pointer starting from the beginning
        int right = str.length() - 1; // Pointer starting from the end

        // Loop while the left pointer is less than the right pointer
        while (left < right) {
            // Compare characters at the left and right pointers
            if (str.charAt(left) != str.charAt(right)) {
                // If characters don't match, it's not a palindrome
                return false;
            }
            // Move the pointers inwards
            left++;
            right--;
        }

        // If the loop completes without finding any mismatch, it's a palindrome
        return true;
    }
}

この新しいコードでは以下のことが行われています。

  • checkPalindrome という別のメソッドを作成しました。このメソッドは String を入力として受け取り、boolean を返します (回文であれば true、そうでなければ false)。これにより、コードがより整理され、再利用可能になります。
  • checkPalindrome 内で、left を 0 (最初の文字のインデックス) に、rightstr.length() - 1 (最後の文字のインデックス) に初期化します。
  • while (left < right) ループは、左のポインタが右のポインタの左にある限り続きます。
  • if (str.charAt(left) != str.charAt(right)) は、現在の leftright の位置の文字を比較します。文字が異なれば、すぐに回文ではないと判断し、false を返します。
  • 文字が一致する場合、ポインタを内側に移動します。left++ で左のポインタをインクリメントし、right-- で右のポインタをデクリメントします。
  • ループが false を返さずに終了した場合、比較したすべての文字が一致したことを意味するので、文字列は回文であり、true を返します。
  • main メソッドでは、異なるテスト文字列を使って checkPalindrome を呼び出し、結果を出力します。

PalindromeChecker.java ファイルを保存します。

次に、ターミナルでプログラムをコンパイルして実行します。

javac PalindromeChecker.java
java PalindromeChecker

以下のような出力が表示されるはずです。

"madam" is a palindrome.
"racecar" is a palindrome.
"hello" is not a palindrome.

これは、二つのポインタアプローチが文字列を完全に反転させることなく、効率的に回文をチェックする方法を示しています。

回文チェックで大文字小文字と非文字を無視する

前のステップでは、回文チェッカーは "madam" や "racecar" のような単純な文字列に対して機能しました。しかし、現実世界の回文 (palindrome) では、大文字小文字や句読点はしばしば無視されます。たとえば、"A man, a plan, a canal: Panama" は回文と見なされます。

このステップでは、非文字の文字を無視し、大文字と小文字を同じように扱うことで、PalindromeChecker を拡張してこれらのケースを処理できるようにします。

WebIDE のエディターで PalindromeChecker.java ファイルを開きます。checkPalindrome メソッドを修正します。

既存の checkPalindrome メソッドを以下の更新版に置き換えます。

    // Method to check if a string is a palindrome using two pointers,
    // ignoring case and non-letter characters
    public static boolean checkPalindrome(String str) {
        // Initialize two pointers
        int left = 0; // Pointer starting from the beginning
        int right = str.length() - 1; // Pointer starting from the end

        // Loop while the left pointer is less than the right pointer
        while (left < right) {
            // Move left pointer past non-letter characters
            while (left < right && !Character.isLetter(str.charAt(left))) {
                left++;
            }

            // Move right pointer past non-letter characters
            while (left < right && !Character.isLetter(str.charAt(right))) {
                right--;
            }

            // If left and right pointers haven't crossed and characters don't match (ignoring case)
            if (left < right && Character.toLowerCase(str.charAt(left)) != Character.toLowerCase(str.charAt(right))) {
                // If characters don't match, it's not a palindrome
                return false;
            }

            // Move the pointers inwards
            left++;
            right--;
        }

        // If the loop completes without finding any mismatch, it's a palindrome
        return true;
    }

行った変更点は以下の通りです。

  • メインの while (left < right) ループ内に二つの while ループを追加しました。
  • 最初の内側の while ループ (while (left < right && !Character.isLetter(str.charAt(left)))) は、left ポインタが right ポインタの左にあり、left ポインタが指す文字が文字でない限り (!Character.isLetter(...))、left ポインタを前に移動させます。
  • 二番目の内側の while ループ (while (left < right && !Character.isLetter(str.charAt(right)))) は、left ポインタが right ポインタの左にあり、right ポインタが指す文字が文字でない限り、right ポインタを後ろに移動させます。
  • 比較文 if (left < right && Character.toLowerCase(str.charAt(left)) != Character.toLowerCase(str.charAt(right))) には、二つの重要なチェックが含まれています。
    • left < right: これは、非文字をスキップした後にポインタが交差していない場合にのみ文字を比較することを保証します。
    • Character.toLowerCase(...): これは、比較する前に両方の文字を小文字に変換し、大文字小文字を効果的に無視します。
  • 文字 (小文字に変換した後) が一致しない場合、false を返します。
  • 文字が一致する場合、両方のポインタを内側に移動させ (left++, right--)、次の文字のペアをチェックします。

次に、main メソッドを修正して、空白、句読点、大文字小文字が混在した文字列をテストします。main メソッド内の既存のテストケースを以下のように置き換えます。

    public static void main(String[] args) {
        String testString = "A man, a plan, a canal: Panama"; // A classic palindrome
        boolean isPalindrome = checkPalindrome(testString);

        if (isPalindrome) {
            System.out.println("\"" + testString + "\" is a palindrome.");
        } else {
            System.out.println("\"" + testString + "\" is not a palindrome.");
        }

        testString = "No 'x' in Nixon"; // Another palindrome
        isPalindrome = checkPalindrome(testString);

        if (isPalindrome) {
            System.out.println("\"" + testString + "\" is a palindrome.");
        } else {
            System.out.println("\"" + testString + "\" is not a palindrome.");
        }

        testString = "hello world"; // Not a palindrome
        isPalindrome = checkPalindrome(testString);

        if (isPalindrome) {
            System.out.println("\"" + testString + "\" is a palindrome.");
        } else {
            System.out.println("\"" + testString + "\" is not a palindrome.");
        }
    }

PalindromeChecker.java ファイルを保存します。

ターミナルで更新されたプログラムをコンパイルして実行します。

javac PalindromeChecker.java
java PalindromeChecker

これで、非文字の文字や大文字小文字が混在していても、回文を正しく識別する以下の出力が表示されるはずです。

"A man, a plan, a canal: Panama" is a palindrome.
"No 'x' in Nixon" is a palindrome.
"hello world" is not a palindrome.

回文チェッカーをより複雑な文字列に対応できるように拡張することに成功しました!

まとめ

この実験 (Lab) では、Java で文字列が回文 (palindrome) かどうかをチェックする方法を学びました。まず、文字列を反転するメソッドを実装し、元の文字列と比較することで回文かどうかを判断する方法を試しました。これには、Java ファイルを作成し、文字列を逆順に繰り返し処理するコードを書き、比較に equals() メソッドを使用することが含まれていました。

次に、新しい反転文字列を作成する必要のない、より効率的な回文チェック方法である二つのポインタ (two-pointer) アプローチを調べました。最後に、大文字小文字と非文字の文字を無視するように回文チェックを拡張し、現実世界のシナリオに対してより堅牢なチェックを行えるようにしました。