CodeSnippet
最大公约数

GCD

GCD 即求最大公约数的算法,是一种辗转相除法,即用较大的数除以较小的数,再用除数除以余数,直到余数为 0 为止,此时除数就是最大公约数。

例如有两个数 12 和 8,12 ÷ 8 = 1,余数为 4,8 ÷ 4 = 2,余数为 0,所以最大公约数为 4。再复杂一点,有两个数 12 和 18,18 ÷ 12 = 1,余数为 6,12 ÷ 6 = 2,余数为 0,所以最大公约数为 6。有两个数 12 和 32 时,32 ÷ 12 = 2,余数为 8,12 ÷ 8 = 1,余数为 4,8 ÷ 4 = 2,余数为 0,所以最大公约数为 4。我们把它们 a,b 列出来:

a = 32, b = 12 mod: 8
a = 12, b = 8  mod: 4
a = 8,  b = 4  mod: 0

代码

function gcd(a: number, b: number): number {
  if (a < b) {
    [a, b] = [b, a];
  }
  while (b !== 0) {
    [a, b] = [b, a % b];
  }
  return a;
}

这里的 [a, b] = [b, a % b] 是一种解构赋值的写法,它的意思是将 [a, b] 解构为 ab,然后将 b 赋值为 a % b,将 a 赋值为 b。拆开来写就是:

a = b;
b = a % b;

可以使用三元运算符和递归来简化代码:

function gcd(a: number, b: number): number {
  return b === 0 ? a : gcd(b, a % b);
}

这里的三元运算符的意思是:如果 b 等于 0,返回 a,否则返回 gcd(b, a % b)

下面是一个使用 GCD 求 12 和 18 的最小公约数的例子:

let a = 12;
let b = 18;
 
gcd(a, b); // 6

对字符串 ABCABCABC 和 ABCABC,求最大公约数,结果为 ABC:

let a = "ABCABCABC";
let b = "ABCABC";
 
gcd(a, b); // "ABC"

当然还有其它语言的实现,如 C++:

int gcd(int a, int b) {
  if (a < b) {
    swap(a, b);
  }
  while (b != 0) {
    int r = a % b;
    a = b;
    b = r;
  }
  return a;
}
def gcd(a, b):
    if a < b:
        a, b = b, a
    while b != 0:
        a, b = b, a % b
    return a
public int gcd(int a, int b) {
  if (a < b) {
    int temp = a;
    a = b;
    b = temp;
  }
  while (b != 0) {
    int r = a % b;
    a = b;
    b = r;
  }
  return a;
}

实例

对于字符串 s 和 t,只有在 s = t + ... + t(t 自身连接 1 次或多次)时,我们才认定 “t 能除尽 s”。

给定两个字符串 str1 和 str2 。返回 最长字符串 x,要求满足 x 能除尽 str1 且 X 能除尽 str2 。如果不存在,返回空字符串 ""。

示例 1:

输入:str1 = "ABCABC", str2 = "ABC"
输出:"ABC"

示例 2:

输入:str1 = "ABABAB", str2 = "ABAB"
输出:"AB"

示例 3:

输入:str1 = "LEET", str2 = "CODE"
输出:""

题解:

function gcdOfStrings(str1: string, str2: string): string {
    // x 是 str1 和 str2 的公因子,那么 x 也是 str1 + str2 的公因子,且 str1 + str2 === str2 + str1 充要
    // 如果不满足,就返回空字符串
    if (str1 + str2 !== str2 + str1){
        return ''
    }
    function gcd(str1: number,str2:number):number {
        if (str1 < str2){
            [str1,str2]=[str2,str1]
        }
        while (str2!==0){
           [str1,str2] = [str2,str1%str2]
        }
        return str1
    }
    return str1.substring(0, gcd(str1.length, str2.length))
}

这里的主要思路是:如果 str1 和 str2 的最大公约数 x 能够整除 str1 + str2,那么 x 也能整除 str1 和 str2。因为 str1 + str2 = str2 + str1,所以如果 x 能够整除 str1 + str2,那么 x 也能整除 str2 + str1。因此,如果 x 能够整除 str1 + str2,那么 x 也能整除 str1 和 str2。 那么如何求出 x 呢?我们可以使用辗转相除法,求出 str1 和 str2 的最大公约数,然后取 str1 和 str2 的最大公约数的子串即可。