C#:繰り返される文字列

Aug 20 2020

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のすべてのテストケースに合格しました。これは他のソリューション、特に私が賛成したソリューションに基づいています。

回答

4 MartinVerjans Aug 20 2020 at 21:46
  1. 入力を検証する必要がありますか?

もしそうなら、あなたはすべてのケースをテストする必要があります:

  • 入力がnullになる可能性があります
  • 入力は空の文字列である可能性があります
  • nは負または0である可能性があります
  1. 変数名

変数名は重要です。変数名はコードをよりよく理解するのに役立ちます。それらをできるだけ小さくする必要はありません。特に、InteliSenseで適切なIDEを選択するのに役立つVisualStudioのようなIDEがある場合。

  • numAs-> aCount
  • レム->残り
  • 担当者->繰り返し
  • sRem-> restString
  1. 早く失敗する

通常、メソッドは「できるだけ早く」残す方がよいでしょう。したがって、作業を行う前に入力検証を実行し、検証されない場合はメソッドを終了する必要があります。同様に、余りが0の場合、すぐに結果を返すことができます。

  1. 整数除算

繰り返しを計算するには、nから余りを引きます。C#で整数除算をチェックする場合、次のことを行う必要はありません。

long repetitions = n / input.length;
  1. 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'));
2 tinstaafl Aug 20 2020 at 05:06

改善すべき点はあまりありません。しかし、私はいくつかのことに気づきました。

条件付きショートカット:

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'));
}
2 iSR5 Aug 21 2020 at 07:25

他の回答と同じ点ですが、これにはもっと簡単な解決策があります。単に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;
}
1 Noname Aug 22 2020 at 01:50

私はすでに書かれているものに多くを追加することはできません、それがパフォーマンスに来るとき以外、あなたは多くの場合、LINQのは(見つけるlong numAs = input.Count(c => c.Equals('a'));)かなり遅いより伝統的に比較することforwhileループ。しかし、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;
}

このアプローチでは、文字列sdata)は1回だけ解析されます。