はじめに
この実験では、Java で文字列が回文 (palindrome) かどうかをチェックする方法を学びます。まずは、文字列を反転させて元の文字列と比較する基本的な方法から始め、さまざまなアプローチを探っていきます。
これを基に、次には二つのポインタ (two-pointer) アプローチを使ったより効率的な手法を学びます。最後に、大文字小文字や非文字の文字を無視して回文チェックを行うように機能を拡張し、チェックをより堅牢なものにします。
この実験では、Java で文字列が回文 (palindrome) かどうかをチェックする方法を学びます。まずは、文字列を反転させて元の文字列と比較する基本的な方法から始め、さまざまなアプローチを探っていきます。
これを基に、次には二つのポインタ (two-pointer) アプローチを使ったより効率的な手法を学びます。最後に、大文字小文字や非文字の文字を無視して回文チェックを行うように機能を拡張し、チェックをより堅牢なものにします。
このステップでは、文字列を反転させ、元の文字列と比較する単純な 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()
メソッドを使用して originalString
と reversedString
を比較します。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" は前後が同じではないため、プログラムは正しくそれを識別しています。
前のステップでは、文字列を完全に反転させて回文 (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 (最初の文字のインデックス) に、right
を str.length() - 1
(最後の文字のインデックス) に初期化します。while (left < right)
ループは、左のポインタが右のポインタの左にある限り続きます。if (str.charAt(left) != str.charAt(right))
は、現在の left
と right
の位置の文字を比較します。文字が異なれば、すぐに回文ではないと判断し、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) アプローチを調べました。最後に、大文字小文字と非文字の文字を無視するように回文チェックを拡張し、現実世界のシナリオに対してより堅牢なチェックを行えるようにしました。