【C#】文字列の操作(入力・一致・重複・配列)

AtCoderの問題から文字列の操作を学んでいこうと思い備忘録も兼ねて書いていきます。

題材はこちら。

ABC063
――――――――――――――――――――――――
問題文
英小文字からなる文字列Sが与えられます。Sに含まれる文字がすべて異なるか判定してください。
制約
• 2≤|S|≤26,ここで|S|はSの長さを表す。
• Sは英小文字のみからなる。
入力
入力は以下の形式で標準入力から与えられる。
S
出力
Sに含まれる文字がすべて異なる場合はyes(英小文字)、そうでない場合はnoと出力せよ。
――――――――――――――――――――――――

解法①:文字列をArray.Sortでソートして前後に重複がないか確認

string型はそのままソートできないので、一度char型の文字列に変換後、ソートを行います。ソートを行うと重複があった場合、重複している文字同士が隣り合っていることになるので、その重複を検出します。
ソートはArray.Sort、string→char型の変換はToCharArrayでできます。

using System;

class AtCoder{
 public static void Main(){

   string s = Console.ReadLine();
   
//char型の配列に変換
   char c = s.ToCharArray();
//ソート
   Array.Sort(c);
   
   bool b = true;
   for (int i = 1; i < c.Length; i++){
       if(c[i] == c[i-1]){
           b = false;
           break;
       }
   }
   if(b) Console.WriteLine("yes");
   else Console.WriteLine("no");
 }
}

解法②:Distinctを用いて重複した要素を削除

LINQのDistinct()を用いて重複要素の削除ができます。
その後、元の配列との長さを比較すればOKです。

using System;
using System.Linq;

class AtCoder{
 public static void Main(){

   string s = Console.ReadLine();
   char[] c = s.ToCharArray();
   
   char[] c2 = c.Distinct().ToArray();
   
   if(c.Length == c2.Length) Console.WriteLine("yes");
   else Console.WriteLine("no");
 }
}

今回、ToCharArrayを用いましたが、LINQのToArrayでも問題はないようです。AtCoder上での実行時間はToCharArrayが22ms、ToArrayが24msで、効率はToCharArrayのほうが高いみたいです。文字列に特化してるからですかね。

参考までにchar型の配列は文字列に変換できます。(下記参照)

char c = new char[] {'a','b','c'};
string s = new String(c);

解法③:Containsで対象の文字列が含まれているか検出

Containsで文字列に指定した文字列が含まれているか検出します。
またi番目の文字を比較する際、0~i-1番目の文字は調べ終わっているため、Substringを使って、i+1番目の文字から調べられるようにしました。
Substringは第一引数に開始点の要素番号、第二引数にその長さをとり、第一引数から、第二引数分の長さの文字列を取り出します。

using System;
using System.Linq;

class AtCoder{
 public static void Main(){

   string s = Console.ReadLine();
   bool b = true;
   string t;
   for(int i = 0; i < s.Length-1; i++){
     t = s.Substring(i + 1, s.Length-i-1);
     
     if(t.Contains(s[i])){
       b = false;
       break;
     }
   }
   
   if(b) Console.WriteLine("yes");
   else Console.WriteLine("no");
 }
}

解法④:HashSetで重複を除外

HashSetはListや配列などと同じように複数の値を扱うことができますが、重複した値の入力ができません。その性質を利用して要素数を比較します。

using System;
using System.Collections.Generic;

class AtCoder{
 public static void Main(){

   string s = Console.ReadLine();
	HashSet<char> h = new HashSet<char>(s);
   
   if(s.Length == h.Count) Console.WriteLine("yes");
   else Console.WriteLine("no");
 }
}

かなりシンプルですね。

解法⑤:for二重ループで全探索

文字が含まれているか力ずくで調べる方法です。
・・・割愛します。


その他はじめて知ったこと

?と:で条件分岐できる書き方があるようです。例えば今回の問題(解法④)の最後に出力するyes、noの判定部分で使うとこうなります。

//条件式 ? trueの時の処理 : falseのときの処理
Console.WriteLine(s.Length == h.Count ? "yes" : "no");

シンプルに書けていいですね。

最後に

AtCoderをC#で解こうとして、思うように進まず苦戦をしており…。アルゴリズム以前に基本的な操作など、引き出しを増やす必要があると思い、問題の解法形式で始めてみました。良さそうな問題があれば取り上げていこうと思います。