팩토리얼 후행 제로 Leetcode 솔루션


난이도 쉽게
자주 묻는 질문 블룸버그
수학

문제 정책

이 문제에서 우리는 n에 얼마나 많은 후행 XNUMX이 있을지 알아 내야합니다!
n이 입력으로 주어집니다.

마치 5에 하나의 후행 XNUMX이있는 것처럼!
다섯! = 5 * 5 * 4 * 3 * 2 = 1

n = 3
0

설명 : 3! = 6, 후행 XNUMX 없음

n = 0
0

설명 : 0! = 1, 후행 XNUMX 없음

 

n에서 후행 10의 수를 찾으려면! , 간단한 방법은 n! 끝에 0이 몇 개 있는지 확인합니다. 이것은 단순히 숫자를 10으로 나누면 나머지 XNUMX을 얻고 마지막 XNUMX을 XNUMX으로 나누어 제거하는 것으로 간단히 할 수 있습니다. 매번 나머지가 XNUMX이 될 때까지 이것을 셀 수 있습니다.

그러나이 접근법은 우리가 n을 알고 있기 때문에 단순한 정수로는 실용적이지 않습니다! 매우 큰 숫자입니다. C ++ 및 Java에서 단순 정수 크기는 기껏해야 64 비트이며 정수를 2 ^ 63 – 1로 제한 할 수 있습니다. 이는 대략 10 ^ 18입니다. Java에서는 BigInteger 클래스를 사용하여 n!과 같은 큰 숫자를 저장할 수 있습니다. 그러나이 무차별 대입 방식은 시간과 공간이 매우 복잡합니다.

여기서 우리는 n에서 후행 XNUMX을 찾는 효율적인 솔루션에 대해 논의 할 것입니다!

접근 방식 1 (계수 계수 5)

후행 10의 총 개수는 10을 곱한 숫자의 계수와 같다고 말할 수 있습니다. 그리고 우리는 모든 2이 두 개의 소수 5와 XNUMX의 곱으로 구성된다는 것을 알고 있습니다.
따라서 숫자에 2의 인수가 몇 개 있는지 알아 내면. 마찬가지로 5의 인수가 몇 개 있습니다. 그런 다음 이들 각각이 결합되어 제품이 10이된다고 말할 수 있습니다. 따라서 후행 2의 총 개수는 최소값 (5 개 개수, XNUMX 개 개수)과 같습니다.

이제 우리는 n!에서 이러한 요소를 세어야합니다.
엔! = 1 * 2 * 3… .. * (n-1) * n

따라서 우리는 2에서 n까지의 모든 숫자를 반복 할 것입니다 (적어도 하나의 2를 포함하는 숫자이기 때문에 2 씩 증가).
각 숫자에 대해 2로 나눌 수있을 때까지 반복해서 2로 나누어 해당 숫자의 분해에서 2의 개수를 찾습니다.
마찬가지로 그 숫자에서 5의 개수를 찾기 위해 우리는 같은 일을 할 것입니다.
이제이 두 카운트 중 최소값을 반환합니다.

이것은 효과가 있지만 약간 최적화 할 수도 있습니다. 우리는 1에서 n까지의 숫자가 있다는 것을 분석 할 수 있으므로 항상 2의 거듭 제곱보다 5의 거듭 제곱을 얻습니다.
4까지처럼 2를 인자 (2와 4)로 갖는 두 개의 숫자를 얻습니다. 5에서 우리는 요소로 5를 갖는 첫 번째 숫자를 얻습니다. 따라서 이것은 유사한 (증가하는) 카운트 차이로 계속 될 것입니다. 다음은 2 개 요인과 5 개 요인 간의 밀도가 어떻게 다른지 보여주는 시각화입니다.

팩토리얼 후행 제로 Leetcode 솔루션

그러므로 우리는 n!에서 2의 개수가 항상 5의 개수보다 크다는 결론을 내릴 수 있습니다.
그래서 우리는 5의 개수 만 찾아야하고 그것은 ans가 될 것입니다. 이렇게하면 작업을 절반으로 줄일 수 있습니다.

Factorial Trailing Zeroes Leetcode 솔루션 구현

C ++ 프로그램

#include <bits/stdc++.h>
using namespace std;

int trailingZeroes(int n) 
{
    int fives=0;
    for(int i=5;i<=n;i+=5)
    {
        int x=i;
        while(x>0 && x%5==0)
        {
            ++fives;
            x/=5;
        }
    }
    return fives;
}

int main() 
{
   cout<<trailingZeroes(5)<<endl;
   return 0; 
}
1

자바 프로그램

class Rextester{
    
    public static int trailingZeroes(int n) 
    {
        int fives=0;
        for(int i=5;i<=n;i+=5)
        {
            int x=i;
            while(x>0 && x%5==0)
            {
                ++fives;
                x/=5;
            }
        }
        return fives;
    }
    
  public static void main(String args[])
    {    	
    System.out.println(trailingZeroes(5));
    }
}
1

Factorial Trailing Zeroes Leetcode 솔루션의 복잡성 분석

시간 복잡성

의 위에) : n까지 1의 배수를 모두 반복합니다. XNUMX 개의 개수를 찾기 위해 log (n) 시간이 걸리는 각 요소에 대해 보일 수 있습니다. 그러나 우리가 확인한 대다수의 숫자에는 XNUMX의 단일 요소 만 포함되어 있기 때문에 O (XNUMX)로 상각됩니다. 따라서 총 시간 복잡도는 O (n)으로 유지됩니다.

공간 복잡성 

O (1) : 추가 메모리가 사용되지 않습니다.

접근법 2 (효율적으로 계수 5)

위의 접근 방식에서 우리는 주어진 n!의 인자로 5의 개수를 찾아야한다는 것을 알았습니다. 위의 접근 방식에서 우리는 5의 배수를 모두 반복하고 각 숫자에 대해 5의 개수를 추가하고 선형 시간으로 ans를 얻었습니다. 우리는 로그 시간으로 ans를 계산할 수있는 마지막 관찰을 할 것입니다.

n에서! (즉, 1에서 n까지) 5로 나눌 수없는 숫자 (0의 개수에 5 기여)가 있고, 5로 나눌 수있는 숫자 (각각 하나의 개수에 기여)가 있고, 다음으로 나눌 수있는 숫자가 있습니다. 25 (이번에는이 많은 숫자에서 하나의 추가 기여), 다음과 같이 125로 나눌 수 있습니다 (이들로부터 하나의 추가 기여).

그러면 우리의 ans는 이러한 모든 기여의 합이 될 것입니다.
이 모든 것을 다음과 같이 요약 할 수 있습니다.
5의 총 개수 = n / 5 + n / 25 + n / 125 +…. 곧
분수의 정수 값이 XNUMX이되기 때문에 분모가 n보다 작을 때까지 갈 것입니다.

단계:
5로 분모 변수를 초기화합니다.
실행 고리, 각 반복에서 n / 분모 값을 결과에 더하고 분모에 5를 곱합니다. 분모 <n이 될 때까지 루프를 실행합니다.

Factorial Trailing Zeroes Leetcode 솔루션 구현

C ++ 프로그램

#include <bits/stdc++.h>
using namespace std;

int trailingZeroes(int n) 
{
    int fives=0;
    int den=5;

    while(den <= n)
    {
       fives += n/den;
       den *= 5;
    }
    return fives;
}

int main() 
{
   cout<<trailingZeroes(5)<<endl;
   return 0; 
}
1

자바 프로그램

#include <bits/stdc++.h>
using namespace std;

int trailingZeroes(int n) 
{
    int fives=0;
    int den=5;

    while(den <= n)
    {
       fives += n/den;
       den *= 5;
    }
    return fives;
}

int main() 
{
   cout<<trailingZeroes(5)<<endl;
   return 0; 
}
1

Factorial Trailing Zeroes Leetcode 솔루션의 복잡성 분석

시간 복잡성

O (로그 (n)) : n 미만이 될 때까지 분모에 5를 곱합니다. 따라서 총 반복 횟수는 log (n)입니다.

공간 복잡성 

O (1) : 추가 메모리가 사용되지 않습니다.