ただいまpaizaにで勉強中。今回は文字列操作です。
まずはこのあたりから。
c は S の何文字目に現れるかという問題。C#ならこれでいいんじゃないかな。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using System; class Program { static void Main() { string s = Console.ReadLine(); string c = Console.ReadLine(); // c は1 文字。 S に 1つだけ含まれることが保証されている int index = s.IndexOf(c); Console.WriteLine(index + 1); } } |
ただ、1文字ずつ調べるというのが問題の趣旨ならこうかな。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
using System; class Program { static void Main() { string s = Console.ReadLine(); string c = Console.ReadLine(); char[] vs = s.ToCharArray(); char c1 = c.ToCharArray()[0]; // c は1 文字。 S に 1つだけ含まれることが保証されている for (int i = 0; i < vs.Length; i++) { if (vs[i] == c1) { Console.WriteLine(i + 1); return; } } } } |
文字列 S と整数 i , j が与えられるので、S の i 文字目から j 文字目までの部分文字列を出力せよという問題。
これもC#に関しては解説なし。ということで解説するとSubstringを使えば一瞬で解決。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using System; class Program { static void Main() { string s = Console.ReadLine(); string[] ij = Console.ReadLine().Split(); int i, j; i = int.Parse(ij[0]); j = int.Parse(ij[1]); // 先頭の文字は1番目とするため1を引いている Console.WriteLine(s.Substring(i - 1, j - i + 1)); } } |
文字列 S , T と、整数 N が与えられるので、S の N 文字目の後ろに T を挿入した文字列を出力せよという問題。文字列を最初からN文字までとN文字以降に分割してあいだにTを挟めばOK。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
using System; class Program { static void Main() { string s = Console.ReadLine(); string t = Console.ReadLine(); int n = int.Parse(Console.ReadLine()); Console.WriteLine(s.Substring(0, n) + t + s.Substring(n)); } } |
文字列 S と整数 i と文字 c が与えられるので、S の i 文字目を c に書き換えよという問題。
これも本質的にはなにも変わりません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using System; class Program { static void Main() { string s = Console.ReadLine(); string[] ic = Console.ReadLine().Split(); int i = int.Parse(ic[0]); string c = ic[1]; Console.WriteLine(s.Substring(0, i-1) + c + s.Substring(i)); } } |
数値を表す文字列 S が与えられるので、 S – 813 の値を求めよという問題。
これはパス。これができないならなぜこれまでの問題はできたのかという話になるからです。
数値 X , Y が与えられるので、X + Y の計算結果の先頭から N 文字目の数字を出力せよという問題。
計算してToString()で文字列に変換。あとはこれまでの応用。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
using System; class Program { static void Main() { int x = int.Parse(Console.ReadLine()); int y = int.Parse(Console.ReadLine()); int n = int.Parse(Console.ReadLine()); Console.WriteLine((x + y).ToString().ToCharArray()[n - 1]); } } |
ToLower()を使えばなんでもないね。
1 2 3 4 5 6 7 8 9 10 |
using System; class Program { static void Main() { string s = Console.ReadLine(); Console.WriteLine(s.ToLower()); } } |
大文字小文字混合の文字列が与えられるので大文字小文字を反転させよという問題。
以下の方法でもいいがあまりいい方法ではない。もっと短く書けないか?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
using System; class Program { static void Main() { string s = Console.ReadLine(); char[] vs = s.ToCharArray(); for (int i = 0; i < vs.Length; i++) { if (vs[i] == 'a') vs[i] = 'A'; else if (vs[i] == 'A') vs[i] = 'a'; else if (vs[i] == 'b') vs[i] = 'B'; else if (vs[i] == 'B') vs[i] = 'b'; // C以降についても同様 } Console.WriteLine(new string(vs)); } } |
そこでa~zとA~Zは文字コードが連続していることを利用して以下のようなコードを書いてみる。しかし管理人が勉強不足なだけで文字が大文字か小文字かを調べる方法が存在した。ちなみに以下のコードはこれはこれで動作する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
using System; class Program { static void Main() { string s = Console.ReadLine(); char[] vs = s.ToCharArray(); for (int i = 0; i < vs.Length; i++) { for (int j = 0; j < 26; j++) { if (vs[i] == 'a' + j) { vs[i] = (char)('A' + j); break; } else if (vs[i] == 'A' + j) { vs[i] = (char)('a' + j); break; } } } Console.WriteLine(new string(vs)); } } |
System.Char.IsUpperメソッドを使えば大文字かどうかわかるのでそうであれば小文字にそうでなければ大文字に変換する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using System; class Program { static void Main() { string s = Console.ReadLine(); char[] vs = s.ToCharArray(); for (int i = 0; i < vs.Length; i++) vs[i] = char.IsUpper(vs[i]) ? char.ToLower(vs[i]) : char.ToUpper(vs[i]); Console.WriteLine(new string(vs)); } } |
文字列 S が与えられるので、 S を整数に変換できる場合には “YES” , そうでない場合は “NO” を出力せよという問題。
文字列をint型に変換できるかどうかで判断できると思い、以下のようなコードを書いたがこれは正しくない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using System; class Program { static void Main() { string s = Console.ReadLine(); try { int _ = int.Parse(s); Console.WriteLine("YES"); } catch { // これでは"123456789123456789"のような文字列は整数なのに例外が発生してしまう Console.WriteLine("NO"); } } } |
「数値に変換できるとは、すべての文字が0~9である」という条件がつけられているので、文字がすべて0~9なのかどうかを調べなければなりません。
言語によっては文字が 0 … 9 の数値であるかどうかを判定する関数があるものもありますが、0 … 9をすべて空文字に置換して置換後に空文字になっているならすべての文字が0 … 9であったと判断する方法はどうでしょうか? これでもテストは通ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
using System; class Program { static void Main() { string s = Console.ReadLine(); for(int i =0; i<=9; i++) s = s.Replace(i.ToString(), ""); if (s == "") Console.WriteLine("YES"); else Console.WriteLine("NO"); } } |
重複を削除するのであえばみんなのアイドル?Linqを使えば簡単にできます。
1 2 3 4 5 6 7 8 9 10 11 12 |
using System; using System.Linq; class Program { static void Main() { string s = Console.ReadLine(); Console.WriteLine(new string(s.ToCharArray().Distinct().ToArray())); } } |
重複排除の処理を自分で実装せよというのが問題の趣旨であるなら、先頭から順にその数字が既に現れたかどうかを調べて、はじめて出現した数字だけを答えである文字列の末尾に追加することで答えを取得することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
using System; class Program { static void Main() { string s = Console.ReadLine(); char[] vs = s.ToCharArray(); // 最初はすべてfalse。使われた数と同じ要素をtrueに変更する bool[] used = new bool[10]; string ret = ""; for (int i = 0; i < vs.Length; i++) { int num = int.Parse(vs[i].ToString()); if (!used[num]) { used[num] = true; ret += vs[i]; } } Console.WriteLine(ret); } } |
ということで、「なんだ、ちょろいじゃん♪」と思ったのだが、やや解きごたえのあるものもあったので紹介する。
次のようなルールにのっとって N 文字のパスワードを設定することにした。
N 文字のうち、 Q 文字だけ覚えておく文字を決めておく。
具体的には n_i 文字目を c_i とだけ決めて、残りの全ての文字を C にする。
パスワードはなにかという問題。
まさかこんな方法でパスワードを決めている人はいないでしょうね。簡単にアカウントを乗っ取られます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
class Program { static void Main() { int n = int.Parse(Console.ReadLine()); int q = int.Parse(Console.ReadLine()); string[] nc = new string[q]; for (int i = 0; i < q; i++) nc[i] = Console.ReadLine(); string c = Console.ReadLine(); // とりあえずすべてcで埋める char[] vs = new char[n]; for (int i = 0; i < n; i++) { vs[i] = c.ToCharArray()[0]; } // 置き換えるべき部分だけ置き換える for (int i = 0; i < q; i++) { string[] vs2 = nc[i].Split(); int index = int.Parse(vs2[0]); char c2 = vs2[1].ToCharArray()[0]; vs[index-1] = c2; // 先頭は0ではなく1なので注意する } Console.WriteLine(new string(vs)); } } |
先頭に必要のない 0 がいくつかついてしまう
小数である数値の末尾に必要のない 0 がいくつかついてしまう
小数である数値に小数点が複数個ついてしまう
このようなデータを正しく訂正せよという問題。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
class Program { static void Main() { string s = Console.ReadLine(); // 小数点が複数ある場合は1つだけにする string[] vs = s.Split('.'); if (vs.Length > 2) { string[] vs1 = new string[vs.Length - 1]; for (int i = 0; i < vs.Length-1; i++) { vs1[i] = vs[i + 1]; } s = vs[0] + "." + string.Join("", vs1); } // 少数の場合は末尾の0を取り除く if (s.IndexOf('.') != -1) s = s.TrimEnd('0'); // 先頭の0を取り除くが少数で先頭が.になってしまう場合は0を補う s = s.TrimStart('0'); if (s.IndexOf('.') == 0) s = "0" + s.TrimStart('0'); // s == "000" のような場合、文字列が消えてしまう。その場合は0とする if (s == "") s = "0"; Console.WriteLine(s); } } |
計算式が”4+3-2+1″のような文字列で渡されるので答えを計算せよという問題。
解説では 文字列を数字を表す文字列と演算子を表す文字列に分ける。
分けた後の数字を表す文字列を数値に変換したのち、演算子に従って計算を行う。
とあるが、加法の結合法則がなりたつので、足し算として分解し、残りの部分で引き算をして最後に全部足すという方法で答えを求めています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class Program { static void Main() { string s = Console.ReadLine(); string[] vs = s.Split('+'); int ret = 0; foreach (string str in vs) { string[] vs2 = str.Split('-'); int value = int.Parse(vs2[0]); for (int i = 1; i < vs2.Length; i++) value -= int.Parse(vs2[i]); ret += value; } Console.WriteLine(ret); } } |
100桁を超える巨大な数同士の足し算。ただし桁数は同じという問題。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
using System; using System.Linq; class Program { static void Main() { string s = Console.ReadLine(); string t = Console.ReadLine(); // 桁数が同じことが保証されている // 1の位から計算しなければならないが面倒くさいので反転させて先頭から計算する char[] vs1 = s.ToCharArray().Reverse().ToArray(); char[] vs2 = t.ToCharArray().Reverse().ToArray(); string ret = ""; int kuri = 0; for (int i = 0; i < vs1.Length; i++) { int a = int.Parse(vs1[i].ToString()); int b = int.Parse(vs2[i].ToString()); int c = a + b + kuri < 10 ? a + b + kuri : a + b + kuri - 10; kuri = (a + b + kuri) / 10; // 繰り上がり ret += c.ToString(); } // 最後の繰り上がりを忘れないようにする if (kuri != 0) ret += kuri; // 反転させて計算しているのでもう一度反転させる。これが答え Console.WriteLine(new string(ret.ToCharArray().Reverse().ToArray())); } } |
最後はかけ算。ラスボス登場かと思いきや巨大な数と一桁のかけ算。これなら簡単だと思っていたら引っかけが。一桁が0の場合を想定していなかった。ということで凡ミスで不合格。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
using System; using System.Linq; class Program { static void Main() { string s = Console.ReadLine(); int t = int.Parse(Console.ReadLine()); // 0とのかけ算なら0。これをしないと答えが000000000・・・となってしまう。 if (t == 0 || (s.Length == 1 && int.Parse(s) == 0)) { Console.WriteLine("0"); return; } char[] vs1 = s.ToCharArray().Reverse().ToArray(); string ret = ""; int kuri = 0; for (int i = 0; i < vs1.Length; i++) { int a = int.Parse(vs1[i].ToString()); int c = (a * t + kuri) % 10; kuri = (a * t + kuri) / 10; // 繰り上がり ret += c.ToString(); } if (kuri != 0) ret += kuri; Console.WriteLine(new string(ret.ToCharArray().Reverse().ToArray())); } } |