C#:繰り返される文字列
HackerRankの「RepeatedString」チャレンジから:
リラには文字列があります、\$s\$、彼女が何度も繰り返した小文字の英字。
与えられた整数、\$n\$、最初の\の文字aの数を見つけて印刷します$n\$ リラの無限の文字列の文字。
たとえば、文字列\$s=abcac\$および\$n=10\$、私たちが考える部分文字列は\$abcacabcac\$、最初の\$10\$彼女の無限の文字列の文字。あります\$4\$ 部分文字列内のaの出現。
テストケース1:
string input = "aba";
long n = 10;
テストケース2:
string input = "a";
long n = 1000000000000;
私の解決策:
string input = "aba";
long n = 10;
long numAs = input.Count(c => c.Equals('a'));
if (input.Length == 0)
{
return 0;
}
long rem = n % input.Length;
long reps = (n - rem) / input.Length;
long count = reps * numAs;
string sRem = input.Substring(0, (int)rem);
if (rem != 0)
{
count += sRem.Count(c => c.Equals('a'));
}
結果は7と1000000000000になるはずです。このソリューションはHackerRankのすべてのテストケースに合格しました。これは他のソリューション、特に私が賛成したソリューションに基づいています。
回答
- 入力を検証する必要がありますか?
もしそうなら、あなたはすべてのケースをテストする必要があります:
- 入力がnullになる可能性があります
- 入力は空の文字列である可能性があります
- nは負または0である可能性があります
- 変数名
変数名は重要です。変数名はコードをよりよく理解するのに役立ちます。それらをできるだけ小さくする必要はありません。特に、InteliSenseで適切なIDEを選択するのに役立つVisualStudioのようなIDEがある場合。
- numAs-> aCount
- レム->残り
- 担当者->繰り返し
- sRem-> restString
- 早く失敗する
通常、メソッドは「できるだけ早く」残す方がよいでしょう。したがって、作業を行う前に入力検証を実行し、検証されない場合はメソッドを終了する必要があります。同様に、余りが0の場合、すぐに結果を返すことができます。
- 整数除算
繰り返しを計算するには、nから余りを引きます。C#で整数除算をチェックする場合、次のことを行う必要はありません。
long repetitions = n / input.length;
- Linqを使用する
tinstaaflソリューションに従って、Linqを使用して変数と行を保存できます。
count += remainderString.Take((int)remainder).Count(c => c.Equals('a'));
したがって、全体として、次のようになります。
long aCount = input.Count(c => c.Equals('a'));
if (input == null || input.Length == 0 || n <= 0)
{
return 0;
}
long repetitions = n / input.Length;
long remainder = n % input.Length;
long count = repetitions * aCount;
if (remainder == 0)
{
return count;
}
return count + remainderString.Take((int)remainder).Count(c => c.Equals('a'));
改善すべき点はあまりありません。しかし、私はいくつかのことに気づきました。
条件付きショートカット:
if (input.Length == 0)
{
return 0;
}
直後にコードの最初のものにする必要があります input
同様に:
string sRem = input.Substring(0, (int)rem);
if (rem != 0)
{
count += sRem.Count(c => c.Equals('a'));
}
rem
> 0でない限り、その文字列は必要ないので、条件付きブロックに含めます。さらに良いことに、LINQ拡張機能を使用して、Take
すべてを1つのステートメントで実行します。
if (rem != 0)
{
count += sRem.Take((int)rem).Count(c => c.Equals('a'));
}
他の回答と同じ点ですが、これにはもっと簡単な解決策があります。単にA
空の文字列に置き換えて、両方の文字列の長さを比較すると、Aの数がわかります。
ここに例があります:
public static long RepeatedString(string s, long n)
{
if (string.IsNullOrWhiteSpace(s) || n <= 0) { return 0; }
// Local function that would return the number of A's
long CountA(string input) => input.Length - input.Replace("a", "").Length;
var aCount = CountA(s);
var reminder = n % s.Length;
var repetition = (n - reminder) / s.Length;
var count = repetition * aCount;
var reminderStr = s.Substring(0, (int)reminder);
var result = count + CountA(reminderStr);
return result;
}
私はすでに書かれているものに多くを追加することはできません、それがパフォーマンスに来るとき以外、あなたは多くの場合、LINQのは(見つけるlong numAs = input.Count(c => c.Equals('a'));
)かなり遅いより伝統的に比較することfor
やwhile
ループ。しかし、Linqを主張する場合は、次のようにすべてを行うことができます。
long CountChars(string data, long length, char c = 'a')
{
if (string.IsNullOrEmpty(data) || length <= 0) return 0;
long repetitions = length / data.Length;
long remSize = length % data.Length;
return data
.Select((ch, i) => (ch, i))
.Where(chi => chi.ch == c)
.Sum(chi => chi.i < remSize ? repetitions + 1 : repetitions);
}
ここでのオーバーロードに使用されるSelect()
ことは、それによりフィルタすることが可能であり、そこから値タプルにマップする各要素と共に指数を提供'a'
インデックスはリマインダーのサイズよりも小さいある場合、:、最終的に繰り返しをまとめrepetitions + 1
なければなりませんそれ以外の場合は、見つかっ'a'
た各の繰り返しのみを合計します。
while
-loopsを使用する従来のアプローチ-基本的に上記と同じアプローチを使用すると、次のようになります。
long CountChars(string data, long length, char c = 'a')
{
if (string.IsNullOrEmpty(data) || length <= 0) return 0;
long count = 0;
long repetitions = length / data.Length + 1; // + 1 for the possible extra 'a' in the reminder
long remSize = length % data.Length;
int i = 0;
while (i < remSize)
{
if (data[i++] == c)
count += repetitions;
}
repetitions--;
while (i < data.Length)
{
if (data[i++] == c)
count += repetitions;
}
return count;
}
このアプローチでは、文字列s
(data
)は1回だけ解析されます。