【问题标题】:Given n; check if it is formed by a number and its reverse给定 n;检查它是否由数字及其反转组成
【发布时间】:2020-03-11 01:06:14
【问题描述】:

给定n。假设存在一个数字x,并考虑x 的数字反转,即y。现在,n = x + y。例如:110(= 46+64)我的问题是如何检查是否存在至少一种将给定数字表示为其他数字之和及其数字反转的方法

【问题讨论】:

  • 这个数字有多大?
  • @giotskhada 抱歉,我不知道数量的限制。这是我在一次采访中被要求编写的代码。您可以在可解决的范围内假设最高难度。
  • 您知道如何解决这种蛮力并寻找更有效的解决方案,还是只是在寻找任何解决方案?因为,我可以为你写出一个蛮力的 O(nlogn) 解决方案,但我不确定你是否需要它。
  • 反向可以有前导零吗?换句话说,是 101 = 100+001 吗?

标签: algorithm


【解决方案1】:

嗯,有一个简单的蛮力解决方案,时间复杂度为O(nlogn),我假设它对于面试设置来说已经足够好了(尽管对于实际的比赛,可能还不够好)。您可以遍历每个数字直到 n 并检查 x 是否是 n-x 的反转。检查需要logn 时间(准确地说是log(10)n,它更低)。这是一个伪代码:

isReverse(string a, string b) {
  if (a.length != b.length) return false;
  for (i = 0; i < a.length; i++){
    if (a[i] != b[a.length - i - 1]) return false;
  }
}

solution (n){
  for (i = 0; i < n; i++){
    if (isReverse(i.toString(), (n-i).toString())) return true;
  }
  return false;
}

【讨论】:

    【解决方案2】:

    关于面试背景:这个问题不是众所周知的算法的常见问题(这不是 KMP 或 Dijsktra 的算法!),我认为绝对没有理由让某人期待一个开箱即用的答案。

    即使您有尝试计算进位的想法,在您看到它有效之前,您也无法确定它是否有效。如果我没记错的话,有一个时间复杂度为 O(log10 N) 的解决方案。我专注于删除进位并从头开始构建算法(和实现):在某些边缘情况下可能仍然存在细微的错误。 (结果非常接近twin question 的接受答案。)

    前言

    每个回文数都可以写成两个镜像数之和吗?

    想到的第一个想法是这样的数字是回文:

      1 2 5
    + 5 2 1
    -------
      6 4 6
    

    但这不是真的,因为我们知道当两位数之和大于或等于 10 时会有进位。

    让我们从一个简单的问题开始:每个回文数都可以写成两个镜像数的和吗?

    假设我们有N = D[2n]...D[n+1]D[n+1]...D[2n],其中D[k] 是第k 个数字。显然,我们可以这样写:

      D[2n]-1 ... D[n+1]-1        1 ...       1
    +       1 ...        1 D[n+1]-1 ... D[2n]-1
    -------------------------------------------
        D[2n]       D[n+1]   D[n+1]       D[2n]
    

    但是如果位数是奇数呢? N = D[2n]...D[n+1]D[n]D[n+1]...D[2n]。我们可以这样写:

      D[2n]-1 ... D[n+1]-1 D[n]/2        1 ...       1
    +       1 ...        1 D[n]/2 D[n+1]-1 ... D[2n]-1
    --------------------------------------------------
        D[2n]       D[n+1]   D[n]   D[n+1]       D[2n]
    

    但这只有在D[n] 是偶数时才可以。

    如果数字不是回文怎么办?

    927 可以写成两个镜像数之和吗?想象一下我们有:

      a b c
    + c b a
    -------
      9 2 7
    

    单位数没有传入进位,因此c+a = 7c+a = 17。在这两种情况下,百位数字将是7(无进位)或8。个位和百位不够接近,无法将数字写成两个镜像数字之和。

    让我们更具体一点。

    N = D[n+1]D[n]...D[0]拥有D[n+1] = 0 or 1(如果第一个数字不是1,我们在数字前面添加0)。

    从小学开始,我们就知道如何将两个数字相加A[n]...A[0]B[n]...B[0]。进位是:

    C[0] = 0
    C[k+1] = (A[k] + B[k] + C[k]) // 10
    

    数字是:

    D[k] = (A[k] + B[k] + C[k]) % 10
    D[n+1] = C[n+1])
    

    如果N = A(n)...A(0) + A(0)...A(n) = M + N - M,我们可以特化上面的命题,因为A[k] = A(k)B[k] = A(n-k)。我们有:

    C[0] = 0
    C[k+1] = (A[k] + A[n-k] + C[k]) // 10       (I)
    

    还有:

    D[k] = (A[k] + A[n-k] + C[k]) % 10
    D[n+1] = C[n+1]
    

    为了使这些公式更有用,我们可以用一种不太常见的方式重写数字公式:

    A[k] + A[n-k] + C[k] = 10*C[k+1] + D[k]         (II)
    

    并将k 替换为n-k

    A[n-k] + A[k] + C[n-k] = 10*C[n-k+1] + D[n-k]
    

    将两个公式放在一起,我们有:

    D[k] - D[n-k] = C[k] - C[n-k] - 10*(C[k+1] - C[n-k+1])      (III)
    

    N 不一定是回文,但我们在D[k]D[n-k] 之间仍然有很强的关系。

    算法示意图

    我们的想法是使用这种关系来计算进位。我们从外到内进行构建。如果进位不是 0 或 1,则无解。

    如果所有进位都是 0 或 1,那么我们删除进位。如果剩下的“数”不是回文或中间有奇数,则无解。

    否则我们可以像上面那样构建解决方案。

    算法

    我们将使用(I)和(III)从C[k+1]C[n-k]进步到C[k]C[n-k+1]。如果我们无法前进,即如果进位不是01,那么假设N = A + (N - A) 是错误的。

    如果n+2的位数是偶数,我们在k = n-k+1时停止。 否则,我们在k = n-k 时停止。

    案例一:C[k+1] = 1 and C[n-k] = 0

    如果D[n-k] = 9

    我们知道A[k] + A[n-k] = 9,因为我们需要一个进位C[n-k] = 1 才能达到19。我们有C[n-k+1] = 0 (III)。命题 (III) 给了我们C[k],但我们已经知道C[k+1] = 1C[k] = 1

    如果D[n-k] &lt;= 8

    我们知道A[k] + A[n-k] &lt;= 8A[k] + A[n-k] &gt;= 10。但是我们有一个进位C[k+1] = 1:唯一的选择是A[k] + A[n-k] &gt;= 10,因此是C[n-k+1] = 1。我们使用(III)计算C[k]D[k] - D[n-k] = C[k],即C[k] = D[k] - D[n-k]

    案例2:C[k+1] = 1 and C[n-k] = 1

    我们有A[k] + A[n-k] + C[k] &gt;= 10,因此有A[k] + A[n-k] &gt;= 9A[k] + A[n-k] + C[n-k] &gt;= 10C[n-k+1] = 1。我们使用 (III) 计算 C[k]C[k] = D[k] - D[n-k] + 1

    案例3:C[k+1] = 0 and C[n-k] = 0

    如果D[n-k] = 9

    我们知道A[k] + A[n-k] = 9,因为我们需要一个进位C[n-k] = 1才能达到19。因此,C[n-k+1] = 0C[k] = 0

    如果D[n-k] &lt;= 8

    我们知道A[k] + A[n-k] &lt;= 8A[k] + A[n-k] &gt;= 10。但是我们没有进位(C[k+1] = 0):唯一的选择是A[k] + A[n-k] &lt;= 8,因此是C[n-k+1] = 0。我们使用(III)计算C[k]D[k] - D[n-k] = C[k] + 10,即C[k] = D[k] - D[n-k]

    案例4:C[k+1] = 0 and C[n-k] = 1

    如果D[n-k] = 9

    我们知道A[k] + A[n-k] = 8A[k] + A[n-k] = 18。但是如果A[k] + A[n-k] = 18,我们将有C[k+1] = 1。因此,A[k] + A[n-k] = 8C[n-k+1] = 0 以及使用 (III)、D[k] - D[n-k] = C[k] - 1,即 C[k] = D[k] - D[n-k] + 1

    如果D[n-k] = 0

    我们知道A[k] + A[n-k] = 9C[n-k+1] = 1。从C[k+1] = 0 开始,我们就有了C[k] = 0

    如果1 &lt;= D[n-k] &lt;= 8

    我们知道0 &lt;= A[k] + A[n-k] &lt;= 710 &lt;= A[k] + A[n-k] &lt;= 17。由于C[k+1] = 0,我们知道0 &lt;= A[k] + A[n-k] &lt;= 7。因此我们有 C[n-k+1] = 0C[k] = 0

    解压

    使用(II)计算S(k) = A[k] + A[n-k]

    A[k] + A[n-k] = D[k] - C[k] + 10*C[k+1]
    

    S(k) 可能大于9,但列表:[S(n), ..., S(0)] 必须是回文。

    如果 是奇数个元素,那么我们需要中间元素是偶数。对于其他元素:

    • 如果S(k) = 0,则设置A[k] + A[n-k] = 0
    • 如果1 &lt;= S(k) &lt;= 10,则设置A[k] = S(k) - 1A[n-k] = 1
    • 如果10 &lt;= S(k) &lt;= 18,则设置A[k] = S(k) - 9A[n-k] = 9

    时间复杂度

    时间复杂度为O(log10 N),因为我们将数字循环了三遍(可能是一到两次)。

    在 Python 中的实现

    实现充满了边缘情况。我测试了 10 到 10000 之间的数字,但可能仍然存在细微的错误。

    def check_mirror_sum(N):
        D = list(map(int, reversed(str(N))))
        if D[-1] > 1:
            D.append(0)
        n = len(D) - 2
        C = [None] * (n+2)
        C[n+1], C[0] = D[n+1], 0
    
        for k in range(n, 0, -1):
            if k - (n-k+1) < 0:
                break
            if C[k+1] == 1:
                if C[n-k] == 0:  # case 1
                    if D[n-k] == 9:
                        e, f = 1, 0
                    else:
                        f = 1
                        e = D[k] - D[n-k]
                else:  # C[n-k] == 1, case 2
                    f = 1
                    e = D[k] - D[n-k] + 1
            else:  # C[k+1] == 0
                if C[n-k] == 0:  # case 3
                    if D[n-k] == 9:
                        e, f = 0, 0
                    else:
                        f = 0
                        e = D[k] - D[n-k]
                else: # C[n-k] == 1, case 4
                    if D[n-k] == 9:
                        f = 1
                        e = D[k] - D[n-k] + 1
                    elif D[n-k] == 0:
                        e, f = 0, 1
                    else:
                        e, f = 0, 0
            if not (0 <= e <= 1 and 0 <= f <= 1) or (k == n-k+1 and e != f):
                return None
    
            C[k], C[n-k+1] = e, f
    
        S = [D[k] - C[k] + 10*C[k+1] for k in range(n+1)]
        A = [None] * (n+1)
        if (n+1) % 2 == 1:
            if S[(n+1)//2] % 2 == 1: # odd middle "digit"
                return None
    
            A[(n+1)//2] = S[(n+1)//2] // 2
        for k in range((n+1)//2):
            if S[k] != S[n-k]: # not a palindrom! 
                return None
            if S[k] == 0:
                A[k], A[n-k] = 0, 0
            elif S[k] <= 10:
                A[k], A[n-k] = S[k] - 1, 1
            else:
                A[k], A[n-k] = S[k] - 9, 9
        if A[0] == 0 or A[-1] == 0: # should not start or end with 0
            return None
    
        M = int("".join(map(str, A)))
        return M
    

    测试:

    def control(N):
        for i in range(1, N):
            s = "".join(reversed(str(i)))
            if s[0] != '0' and i + int(s) == N:
                return i
    
        return None
    
    
    for N in range(10, 10000):
        M = check_mirror_sum(N)
        M2 = control(N)
        assert (M is None) == (M2 is None)
    
        if M is not None:
            print("{} = {} + {}".format(N, M, N-M))
    

    输出:

    10 = 5 + 5
    12 = 6 + 6
    14 = 7 + 7
    16 = 8 + 8
    18 = 9 + 9
    ...
    9779 = 8611 + 1168
    9878 = 7891 + 1987
    9889 = 8711 + 1178
    9988 = 7991 + 1997
    9999 = 8811 + 1188
    

    【讨论】:

      猜你喜欢
      • 2019-12-17
      • 2021-01-30
      • 1970-01-01
      • 1970-01-01
      • 2023-03-17
      • 2013-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多