問1.与えられた文字列の小文字と大文字を入れ替えるプログラムを作成してください。
- input
文字列が1行に与えられます。 - output
与えられた文字列の小文字と大文字を入れ替えた文字列を出力して下さい。アルファベット以外の文字はそのまま出力して下さい。
PHP
AOJ.php
<?php
$arr = str_split(trim(fgets(STDIN)));
for ($i = 0; $i < count($arr); $i++) {
if (ctype_upper($arr[$i])) {
$arr[$i] = strtolower($arr[$i]);
} elseif (ctype_lower($arr[$i])) {
$arr[$i] = strtoupper($arr[$i]);
}
}
echo implode("", $arr);
?>
- str_split():文字列を1文字ずつ配列に分割するメソッド
- ctype_upper,ctype_lower():文字列がすべて大文字、小文字かどうか判定
- PHPでは 文字列全体に対して直接 ctype_upper() や ctype_lower() を適用すると、すべての文字が大文字または小文字でなければ false を返す ため、1文字ずつ分割して判定する必要がある。
- strtolower,strtoupper:string のアルファベット部分をすべて小文字,大文字にして返す
別解
other.php
<?php
$str = trim(fgets(STDIN));
//文字列 $str の長さを strlen($str) で取得
for ($i = 0, $len = strlen($str); $i < $len; $i++) {
//文字列 $str の $i 番目の文字を $c という変数に格納
$c = $str[$i];
//文字 $c が小文字(a~z)であるかどうかをチェック
if (preg_match('/[a-z]/', $c)) {
echo strtoupper($c);
} elseif (preg_match('/[A-Z]/', $c)) {
echo strtolower($c);
} else {
echo $c;
}
}
echo PHP_EOL;
Java
AOJ.java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()) { // 複数行入力に対応
String input = sc.nextLine();
char[] arr = input.toCharArray(); // 文字列を char 配列に変換
// 大文字⇔小文字の変換処理
for (int i = 0; i < arr.length; i++) {
if (Character.isUpperCase(arr[i])) {
arr[i] = Character.toLowerCase(arr[i]);
} else if (Character.isLowerCase(arr[i])) {
arr[i] = Character.toUpperCase(arr[i]);
}
}
//char[] 配列の中に格納された文字を 1つの文字列として結合
System.out.println(new String(arr));
}
sc.close();
}
}
- 一文字ずつ分割してchar型配列にいれる→String型配列にする...配列には空白も一文字としてカウントされ入っているためStringになおして出力したとき、入力時と同じ表示になる
- Character.isUpperCase()...引数に指定した文字が大文字か判別
- Character.toUpperCase()...引数に指定した文字を大文字に変換
- String は、複数の文字(char)を 結合したもの。そのためnew String(arr)が実行されるとarrに入っていた文字が全て結合され文字列となる。
別解
other.java
import java.io.*;
class Main {
public static void main (String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//sb...入力を読み取り文字列変更の得意なStringBuilderオブジェクトとして生成したもの
StringBuilder sb = new StringBuilder(br.readLine());//// 標準入力から一行読み取る
//文字列の長さ分だけ繰り返し処理
//charAt(i) メソッドを使って、StringBuilder から1文字ずつ取り出し大文字か小文字かを判定
for (int i = 0; i < sb.length(); i++) {
char c = sb.charAt(i);
if (Character.isUpperCase(c)) {
sb.setCharAt(i, Character.toLowerCase(c));
} else {
sb.setCharAt(i, Character.toUpperCase(c));
}
}
System.out.println(sb.toString());
}
}
- バッファリング処理
- →書き込み時であればデータを一旦メモリー(バッファ)に蓄積し、いっぱいになったところでファイルに出力する。
- →読み込み時であればバッファにまとめてデータを読み込み、いっぱいになったところでデータを処理する。
- いずれの場合もデータをまとめて処理できるので、読み書きを効率化できる。
- このようなバッファリングを担うのが、BufferedReader/BufferedWriterクラス
- BufferedReaderクラスは、テキストファイルを読み込むために用いられる。その中でもreadlineメソッドはテキストファイルを1行ずつ読み込みString型の戻り値として返す。ファイルの終わりに到達した場合は「null」を返すため、ファイルの終わりまで読み込む場合には、while文でnullが返されるまでループさせる。
- System.in は InputStream 型のオブジェクトで、バイト単位でデータを読み取るため、文字(文字列)をそのまま読み取ることはできない。そこで、バイトストリーム(InputStream)を文字ストリーム(Reader)に変換するInputStreamReaderクラスを用いることでSystem.in(バイト単位で入力を受け取る)から、文字単位で入力を扱えるようにする。
- Scannerは高レベルなクラスでInputStreamReaderは低レベルなクラスだがその分パフォーマンスが◎(ただし手動でバッファリングや読み取りの処理を管理する必要がある)
- String は変更不可(イミュータブル)なクラスで、文字列の変更を行うたびに新しいオブジェクトが作成されてしまうが、StringBuilder は可変(ミュータブル)な文字列を扱うことができる
- setCharAt() メソッドは、StringBuilder クラスにおけるメソッド。指定したインデックスに位置する文字を、新しい文字に置き換えるために使用。今回は文字の小文字版(または大文字版)をインデックスに位置する文字に置き換えるために使用。
other2.java
import java.util.*;
class Main {
public static void main (String[] args) throws java.lang.Exception {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
for(int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
//文字 c が小文字(a から z の範囲)かどうかを判定
//小文字であれば、c - 32 の式を使ってその文字を大文字に変換し出力
if('a' <= c && c <= 'z') System.out.print((char)(c - 32));
else if('A' <= c && c <= 'Z') System.out.print((char)(c + 32));
else System.out.print(c);
}
System.out.println();
}
}
- ASCII値...コンピュータや通信機器が文字を内部で表現するために使う数値の規格。ASCII は、英数字や記号、制御文字を含む、128の文字とその対応する数値(0から127までの整数)を定義している。
- 小文字の 'a' は ASCII 値で 97 だが、大文字の 'A' は 65 なので、c - 32 で小文字を大文字に変換できる。
- 同様に、大文字の 'A' は ASCII 値で 65 だが、小文字の 'a' は 97 なので、c + 32 で大文字を小文字に変換できる。
問2.与えられた数の各桁の和を計算するプログラムを作成して下さい。
- 複数のデータセットが入力として与えられます。各データセットは1つの整数 x を含む1行で与えられます。
- x が 0 のとき入力の終わりとします。
PHP
AOJ.php
<?php
while(true){
$x = trim(fgets(STDIN));
if($x == "0") break;
$num = str_split($x);
echo array_sum($num)."\n";
}
?>
- explodeは空文字""で分割することはできない
- 入力は"0"だから条件分岐として「配列の合計値が0じゃないなら(0のみの入力じゃないなら)」は型の違いからうまく作動しない
- 分割した文字にarray_mapでintvalを適用してからarray_sumしていたが、調べてみたところPHPは array_sum() を使用する際、文字列が数値に自動変換されるらしいのでarray_mapは不要
- while を使うのは、複数回入力を受け取り、その都度処理を行い、特定の条件(この場合は入力が "0" の時)で終了するため。問題文に複数のデータセットが入力として与えられるとあるので複数回入力を受け取る必要がある
Java
AOJ.java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true) {
String x = sc.nextLine();
if (x.equals("0")) break;
int sum = 0;
for (char c : x.toCharArray()) {
sum += c - '0';
}
System.out.println(sum);
}
sc.close();
}
}
- x = "123" の場合、toCharArray() によって ['1', '2', '3'] になる。
- c - '0' を計算すると、その文字の数値としての値が取得できる
- 例: c = '3' なら、'3'= 51,'0'=48なので'3' - '0'は51 - 48 = 3
- Javaの整数型(int や long)では1000桁の数を扱えないため扱える桁数を考慮して今回は文字列として扱い、toCharArrayで各桁を計算する方法を取った。
問3.与えられた文字列の列に含まれる、各アルファベットの数を数えるプログラムを作成して下さい。
小文字と大文字は区別しません。
- input
複数の文字列が与えられます。入力は複数行で与えられる場合があります。 - output
入力に含まれる各アルファベットの数を以下に示す形式で出力して下さい:
a : aの個数
b : bの個数
c : cの個数
.
.
z : zの個数
PHP
AOJ.php
<?php
$input = '';
while ($line = fgets(STDIN)) {
$input .= $line;
}
// 小文字に変換して、大文字・小文字を区別しないようにする
$input = strtolower($input);
// 連想配列をつくる
$counts = array_fill_keys(range('a', 'z'), 0);
// 各文字をカウント
for ($i = 0; $i < strlen($input); $i++) {
$char = $input[$i];
if ($char >= 'a' && $char <= 'z') {
$counts[$char]++;
}
}
foreach (range('a', 'z') as $letter) {
echo "$letter : {$counts[$letter]}\n";
}
//小文字にして大文字小文字の区別なくす
//a~zの連想配列を作る
//入力値の一文字ずつをチェックして配列のキーに該当するアルファベットがあれば値をカウント
//出力
?>
- rangeは整数やアルファベットの始まりと終わりを指定し、その範囲のものを順番に格納した配列を返す。
今回は使わないが第三引数まで用意して偶数、奇数を用意することもできるので結構便利。 - array_fill_keysは配列の全ての値を、第2引数に指定した値で埋める。
- →配列の各要素(アルファベット)をキーとして、対応する値(出現回数)を 0 に初期化した連想配列 $counts を作成
別解
other.php
<?php
$alphabet = array_combine(range('a', 'z'), array_fill(0, 26, 0));
while (($line = trim(fgets(STDIN))) !== '') {
$line = strtolower($line);
for ($i = 0, $len = strlen($line); $i < $len; $i++) {
$c = $line[$i];
if (isset($alphabet[$c])) {
$alphabet[$c]++;
}
}
}
foreach ($alphabet as $key => $val) {
printf("%s : %d\n", $key, $val);
}
- array_combine : 一方の配列をキーとして、もう一方の配列を値として、ひとつの配列を生成する
Java
AOJ.java
import java.util.Scanner;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
TreeMap<Character, Integer> arr = new TreeMap<>();
// a~z のキーを作成(初期値 0)
for (char c = 'a'; c <= 'z'; c++) {
arr.put(c, 0);
}
// 入力の全文字列を読み込む(1行ずつ取得し、StringBuilder に追加)
StringBuilder allInput = new StringBuilder();
while (sc.hasNextLine()) {
allInput.append(sc.nextLine());
allInput.append("\n"); // 改行を保持
}
// 小文字に変換
String input = allInput.toString().toLowerCase();
// 文字ごとにカウント
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (c >= 'a' && c <= 'z') {
//arr.get(c) はキー c に対応する値を取得
//arr.put(c, value) は、キー c に対応する値を value に設定する
arr.put(c, arr.get(c) + 1);
}
}
// 結果の出力
for (char key : arr.keySet()) {
System.out.println(key + " : " + arr.get(key));
}
sc.close();
}
}
- 連想配列構造で自然順序を実現したいのでTreeMapクラスを使用
- Javaにはissetメソッドはない。代わりに使うならcontainsKey(c)
- TreeMapの要素数分ループさせるならlengthではなくsize()
- a~zの自然順序はforループでも使える(型はchar)
- toString() で StringBuilder を String に変換→toLowerCase() で すべての文字を小文字に変換(大文字小文字の区別をなくす
- StringBuilder を使うと、 複数行の入力を1つの文字列としてまとめて(連結)してから処理する というアプローチが可能。また、出力時にその連結に区切りが必要になるので今回のように改行やスペースをappendで追加しながら入力を受け取る。また、今回のコードではStringBuilderは必須というわけではないため普通にwhileループで一行ずつ処理しても動く。
StringBuilder sb = new StringBuilder();
sb.append("hello");
sb.append("\n"); // 改行を挿入
sb.append("java");
System.out.println(sb.toString());
※append("\n")をいれないとhellojavaという一文のまま
hello
java
- 今回はマップを使ったがアルファベットは26個って決まっているので配列でもできる
問4.リング
リング状の文字列sの任意の位置から、時計回りに連続した文字をいくつか選んで、文字列pが作れるかを判定するプログラムを作成してください。
- input
1行目に文字列sが与えられます。
2行目に文字列pが与えられます。 - output
pが作れる場合は Yes と、作れない場合は No と1行に出力してください。
PHP
AOJ.php
<?php
$input = trim(fgets(STDIN));
$s = $input.$input;
$p = trim(fgets(STDIN));
if (strpos($s, $p) !== false) {
echo "Yes\n";
} else {
echo "No\n";
}
//文字列sの入力をうけとる abcd
//文字列s+sをつくる abcdabcd
//文字列Pを受け取り、s+sの中にあればYes,なければNOと表示
?>
- str_contains() は PHP 8.0 以降で追加 された関数なので環境によっては動かない。なので今回は代わりにstrpos()を使う。ただし、strposには戻り値注意点がある。
- 検索文字列が見つかった場合⇛返り値が「文字列が見つかった位置」を整数で返す(開始位置は0)
→整数の「0」はIF判定で「否定」と捉えられ、PHPでは整数「0」はboolean変換において、falseに変換される - 検索文字列が見つからなかった場合⇛falseを返す
具体的には
if (strpos("abcde", "a")) {
echo "Yes";
} else {
echo "No";
}
No
- "abcde" の "a" は 0番目 にあることから戻り値は0、つまりif文内ではfalseと判定され本来はYesと返されれるべきにも関わらずNoと返ってしまう
- 今回のように!== を使うことで、0 でもfalseではない限りtrue判定することを明示することで対処
Java
AOJ.java
import java.util.Scanner;
public class Main{
public static void main(String[]args){
Scanner sc =new Scanner(System.in);
String input = sc.nextLine();
String s = input+input;
String p = sc.nextLine();
if(s.contains(p)){
System.out.println("Yes");
}else{
System.out.println("No");
}
}
}