Check If Digits Are Equal in String After Operations II

Check If Digits Are Equal in String After Operations II

You are given a string s consisting of digits. Perform the following operation repeatedly until the string has exactly two digits:

  • For each pair of consecutive digits in s, starting from the first digit, calculate a new digit as the sum of the two digits modulo 10.
  • Replace s with the sequence of newly calculated digits, maintaining the order in which they are computed.

Return true if the final two digits in s are the same; otherwise, return false.

 

Example 1:

Input: s = "3902"

Output: true

Explanation:

  • Initially, s = "3902"
  • First operation:
    • (s[0] + s[1]) % 10 = (3 + 9) % 10 = 2
    • (s[1] + s[2]) % 10 = (9 + 0) % 10 = 9
    • (s[2] + s[3]) % 10 = (0 + 2) % 10 = 2
    • s becomes "292"
  • Second operation:
    • (s[0] + s[1]) % 10 = (2 + 9) % 10 = 1
    • (s[1] + s[2]) % 10 = (9 + 2) % 10 = 1
    • s becomes "11"
  • Since the digits in "11" are the same, the output is true.

Example 2:

Input: s = "34789"

Output: false

Explanation:

  • Initially, s = "34789".
  • After the first operation, s = "7157".
  • After the second operation, s = "862".
  • After the third operation, s = "48".
  • Since '4' != '8', the output is false.

 

Constraints:

  • 3 <= s.length <= 105
  • s consists of only digits.

 

解题思路

  赛时一直惯性思维把模数认为是质数求组合数,结果样例一直过不了。然后 debug 很久才意识到 10 不是质数,但发现问题后还是不会算组合数模非质数的结果。今年 lc 明显比之前难了不少,我更是直接从 2700 俯冲到了 2400,再打多几场估计就掉到 2300 了。

  之前做过类似的,这类题在计算过程中不要把结果直接求和,而是用每一项表示出来。手推一下会发现每一项的系数其实就是杨辉三角的某一行,其中杨辉三角的第 $i$ 行第 $j$ 列的系数是 $C_{i}^{j}$(行列标号均从 $0$ 开始)。

  由于最后会得到两个数,因此就需要分别计算 $s_0 \sim s_{n-2}$ 的结果 $\sum\limits_{i=0}^{n-2}{C_{n-2}^{i} s_i}$,是否与 $s_{1} \sim s_{n-1}$ 的结果 $\sum\limits_{i=0}^{n-2}{C_{n-2}^{i} s_{i+1}}$ 相同。

  但现在需要对组合数 $C_{a}^{b} = \frac{a!}{b!(a-b)!}$ 模 $10$,意味着需要求阶乘在模 $10$ 意义下的乘法逆元,而 $10$ 不是质数因此费马小定理不适用。又因为只有阶乘与模数 $10$ 互质才存在逆元,因此很有可能是不存在逆元的!但组合数是一个整数,因此一定是有其他做法的。

  当模数不是质数时,有欧拉定理 $x^{\varphi(m)} \equiv 1 \pmod{m}$,因此只要满足 $\gcd(x,m)=1$,那么 $x$ 的逆元就是 $x^{\varphi(m)-1}$(如果 $m$ 是质数那么就变成费马小定理了)。因此对于一个数 $x$,当 $m=10$ 时可以把质因子 $2$ 和 $5$ 提取出来,得到 $x = x' 2^{c_2} 5^{c_5}$ 的形式,那么 $x \bmod m$ 的结果就是 ${x'}^{\varphi(m)-1} 2^{c_2} 5^{c_5} \bmod m$。

  因此对于组合数 $C_{a}{b} = \frac{a!}{b!(a-b)!} = \frac{x}{yz}$,其模 $m=10$ 的结果就是 $\frac{x' 2^{x_2} 5^{x_5}}{y' 2^{y_2} 5^{y_5} \cdot z' 2^{z_2} 5^{z_5}} \equiv x' \, {y'}^{\varphi(m)-1} \, {z'}^{\varphi(m)-1} \, 2^{x_2-y_2-z_2} \, 5^{x_5-y_5-z_5} \pmod{m}$。由于组合数是整数,$x_2-y_2-z_2$ 和 $x_5-y_5-z_5$ 必然大于等于 $0$。

  所以我们需要预处理 $0 \sim n-1$ 每个数的阶乘中,$2$ 的质因子个数,$5$ 的质因子个数,以及阶乘除去质因子 $2$ 和 $5$ 的结果。

  AC 代码如下,时间复杂度为 $O(n \log{n})$:

class Solution {
public:
    bool hasSameDigits(string s) {
        int n = s.size();
        vector<int> f(n), c2(n), c5(n);
        f[0] = 1;
        for (int i = 1; i < n; i++) {
            c2[i] = c2[i - 1];
            c5[i] = c5[i - 1];
            int t = i;
            while (t % 2 == 0) {
                c2[i]++;
                t /= 2;
            }
            while (t % 5 == 0) {
                c5[i]++;
                t /= 5;
            }
            f[i] = f[i - 1] * t % 10;
        }
        vector<int> inv(10, 1);
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 3; j++) {
                inv[i] = inv[i] * i % 10;
            }
        }
        vector<int> p2(n), p5(n);
        p2[0] = p5[0] = 1;
        for (int i = 1; i < n; i++) {
            p2[i] = p2[i - 1] * 2 % 10;
            p5[i] = p5[i - 1] * 5 % 10;
        }
        auto get = [&](int l, int r) {
            int ret = 0;
            for (int i = l; i <= r; i++) {
                ret = (ret + (s[i] - '0') * f[r - l] * inv[f[i - l]] * inv[f[r - i]] * p2[c2[r - l] - c2[i - l] - c2[r - i]] * p5[c5[r - l] - c5[i - l] - c5[r - i]]) % 10;
            }
            return ret;
        };
        return get(0, n - 2) == get(1, n - 1);
    }
};
class Solution {
public:
    bool hasSameDigits(string s) {
        int n = s.size();
        vector<int> p2(n), p5(n);
        p2[0] = p5[0] = 1;
        for (int i = 1; i < n; i++) {
            p2[i] = p2[i - 1] * 2 % 10;
            p5[i] = p5[i - 1] * 5 % 10;
        }
        int ret = s[0] - s[1], c = 1, c2 = 0, c5 = 0;
        auto get = [&](int &x, int p) {
            int ret = 0;
            while (x % p == 0) {
                ret++;
                x /= p;
            }
            return ret;
        };
        for (int i = 1; i + 1 < n; i++) {
            int a = n - 1 - i, b = i;
            c2 += get(a, 2) - get(b, 2);
            c5 += get(a, 5) - get(b, 5);
            c = c * a % 10;
            for (int i = 1; i < 10; i++) {
                if (i * b % 10 == 1) {
                    c = c * i % 10;
                    break;
                }
            }
            ret = (ret + c * (s[i] - s[i + 1]) * p2[c2] * p5[c5]) % 10;
        }
        return !ret;
    }
};

 

参考资料

  组合数模 10【力扣周赛 438】:https://www.bilibili.com/video/BV1hiAUeWEUG/

posted @ 2025-02-24 11:12  onlyblues  阅读(39)  评论(0)    收藏  举报
Web Analytics