C # : 반복되는 문자열
HackerRank "반복 문자열"도전에서 :
Lilah는 문자열을 가지고 \$s\$, 그녀가 무한히 여러 번 반복 한 소문자 영문.
정수가 주어지면 \$n\$, 첫 번째 \ 에서 문자 a의 수를 찾아 인쇄하십시오.$n\$ Lilah의 무한 문자열의 글자.
예를 들어, 문자열 \$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 일 수 있습니다.
- 변수 이름
변수 이름은 중요하며 코드를 더 잘 이해하는 데 도움이됩니다. 가능한 한 작게 만들 필요는 없습니다. 특히 VisualStudio와 같은 IDE가 있으면 InteliSense에서 적절한 IDE를 선택하는 데 도움이됩니다.
- numAs-> aCount
- rem-> 나머지
- 반복-> 반복
- sRem-> 나머지 문자열
- 빠른 실패
일반적으로 "가능한 한 빨리"메소드를 남겨 두는 것이 좋습니다. 따라서 작업을 수행하기 전에 입력 유효성 검사를 수행하고 유효성 검사가되지 않으면 메서드를 종료하려고합니다. 같은 방식으로 나머지가 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
하나의 문으로 모든 작업을 수행하는 것입니다.
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
루프를 사용하는 전통적인 접근 방식 -본질적으로 위와 동일한 접근 방식을 사용하는 것은 다음과 같습니다.
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
)이 한 번만 구문 분석됩니다.