C #: Tekrarlanan Dize
HackerRank "Tekrarlanan Dize" meydan okumasından:
Lilah'ın bir dizesi var, \$s\$, sonsuz sayıda tekrarladığı küçük harfli İngilizce harflerden.
Bir tam sayı verildiğinde, \$n\$, ilk satırdaki a harflerinin sayısını bulun ve yazdırın \$n\$ Lilah'ın sonsuz dizesinin harfleri.
Örneğin, dizge \$s=abcac\$ve \$n=10\$, dikkate aldığımız alt dize \$abcacabcac\$, ilk \$10\$sonsuz dizesinin karakterleri. Var \$4\$ alt dizede a'nın oluşumları.
Test senaryosu 1:
string input = "aba";
long n = 10;
Test senaryosu 2:
string input = "a";
long n = 1000000000000;
Çözümüm:
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'));
}
Sonuçlar 7 ve 1000000000000 olmalıdır. Bu çözüm HackerRank'teki tüm test durumlarını geçti. Diğer çözümlere, özellikle de yukarı oy kullandığım çözümlere dayanıyor.
Yanıtlar
- Girişleri doğrulamanız gerekiyor mu?
Öyleyse, tüm vakaları test etmelisiniz:
- giriş boş olabilir
- girdi boş bir dize olabilir
- n negatif veya 0 olabilir
- Değişken isimler
Değişken isimleri önemlidir, kodu daha iyi anlamaya yardımcı olurlar. Onları olabildiğince küçültmek zorunda değilsiniz. Özellikle, InteliSense ile doğru olanı seçmenize yardımcı olacak VisualStudio gibi bir IDE'niz olduğunda.
- numAs -> aCount
- rem -> kalan
- tekrarlar -> tekrarlar
- sRem -> kalanString
- Hızlı başarısız
"Mümkün olan en kısa sürede" bir yöntem bırakmak genellikle daha iyidir. Bu nedenle, herhangi bir iş yapmadan önce giriş doğrulaması yapmak ve onaylamazsa yöntemden çıkmak istiyorsunuz. Aynı şekilde, kalanınız 0 ise, sonucunuzu hemen iade edebilirsiniz.
- Tamsayı bölümü
Tekrarınızı hesaplamak için, kalanı n'den çıkarırsınız. C # 'da tamsayı bölmesini kontrol ederseniz , şunları yapmanız gerekmez:
long repetitions = n / input.length;
- Linq kullan
Tinstaafl çözümüne göre , bir değişken ve bir satırı kaydetmek için Linq'i kullanabilirsiniz:
count += remainderString.Take((int)remainder).Count(c => c.Equals('a'));
Sonuç olarak, şunları elde edersiniz:
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'));
İyileştirecek pek bir şey görmüyorum. Ancak, birkaç şey fark ettim:
Kısayol koşullu:
if (input.Length == 0)
{
return 0;
}
hemen ardından kodunuzdaki ilk şey olmalıdır input
Benzer şekilde:
string sRem = input.Substring(0, (int)rem);
if (rem != 0)
{
count += sRem.Count(c => c.Equals('a'));
}
Bu dizgeye rem> 0 olmadıkça ihtiyacınız yoktur , bu yüzden onu koşullu bloğa dahil edin. Daha da iyisi, LINQ uzantısını kullanın Takeve her şeyi tek bir ifadede yapın:
if (rem != 0)
{
count += sRem.Take((int)rem).Count(c => c.Equals('a'));
}
Diğer cevaplarla aynı noktalar, ancak bunun daha basit bir çözümü var, basitçe Aboş dizgiyle değiştirebilir ve her iki dizginin uzunluğunu karşılaştırabilirsiniz, bu size A sayısını verir.
İşte bir örnek :
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;
}
Performans söz konusu olduğunda daha önce yazılanlara pek bir şey ekleyemem, genellikle Linq ( long numAs = input.Count(c => c.Equals('a'));) 'nin daha geleneksel forveya whiledöngüye kıyasla oldukça yavaş olduğunu göreceksiniz . Ama Linq'te ısrar ederseniz, her şeyi şöyle yapabilirsiniz:
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);
}
Burada, Select()indeksin, her öğeyle birlikte, filtrelemenin mümkün olduğu bir değer demetine eşlenmesini sağlayan 'a've son olarak tekrarları özetleyen aşırı yüklemesi kullanılır : indeks, hatırlatıcının boyutundan daha küçükse, o zaman repetitions + 1olmalıdır aksi takdirde sadece bulunan her biri için tekrarlar özetlenir 'a'.
while-Döngüler kullanan geleneksel bir yaklaşım - esasen yukarıdaki ile aynı yaklaşımı kullanmak şöyle görünebilir:
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;
}
Bu yaklaşımla dize s( data) yalnızca bir kez çözümlenir.