查找非常大数字是否可被7整除的有效算法

时间:2016-08-01 11:39:36

标签: java algorithm time-complexity dynamic-programming division

所以这是几天前我在网上比赛中遇到的挑战之一的问题。

问题:

接受两个输入。

  1. 大量 N 数字,
  2. 要询问的问题数量 Q
  3. 在每个问题中,你必须找出索引 L i 之间字符串形成的数字> R i 可被整除7或不。

    输入:

    第一行包含 N 数字所包含的数字。下一行包含 Q ,表示问题的数量。每个 Q 行包含2个整数 L i < / sub>和 R i

    输出:

    对于每个问题,如果索引 L i之间的字符串形成的数字,则打印“YES”或“NO” R i 被7整除。

    约束:

    1≤N≤10 5

    1≤Q≤10 5

    1≤L i ,R i ≤N

    示例输入:

    357753
    3
    1 2
    2 3
    4 4

    示例输出:



    是的

    解释

    对于第一个查询,数字将为35,显然可以被7整除。

    每个输入文件的

    时间限制: 1.0秒。

    内存限制: 256 MB

    来源限制: 1024 KB

    我的方法:

    现在根据约束,数字的最大长度,即 N 可以达到 10 5 。这个大数字不能适用于数字数据结构,我很确定这不是有效的方法。

    首先尝试:

    我想到了这个算法,将通用的除法规则应用于数字的每个数字。这将在线性时间内检查任意两个数字之间的可分性,即 O(N)

    static String isDivisibleBy(String theIndexedNumber, int divisiblityNo){
    
        int moduloValue = 0;
    
        for(int i = 0; i < theIndexedNumber.length(); i++){
    
            moduloValue = moduloValue * 10;
            moduloValue += Character.getNumericValue(theIndexedNumber.charAt(i));
            moduloValue %= divisiblityNo;
        }
    
        if(moduloValue == 0){
            return "YES";
        } else{
            return "NO";
        }
    
    }
    

    但在这种情况下,算法还必须循环遍历 Q 的所有值,这些值也可以高达 10 5

    因此,解决问题所需的时间变为 O(Q.N),也可以视为二次时间。因此,这超过了给定的时间限制,效率不高。

    第二次尝试:

    在此之后无效,我尝试搜索 divisibility rule of 7 。我发现的所有内容都涉及基于数字的每个数字的计算。因此,这将再次导致线性时间算法。因此,结合问题的数量,它将相当于二次时间,即O(Q.N)

    我确实找到了一个名为 Pohlman-Mass可分解方法的算法7 ,这表明

      

    使用快速交替添加和减法:
    42,341,530    - &GT; 530-341 = 189 + 42 = 231 - &gt; 23 - (1×2)= 21是

    但所有这一切都是,将时间设为1/3 Q.N,这没有多大帮助。

    我在这里遗漏了什么吗?任何人都可以帮我找到解决这个问题的方法吗?

    此外,这是否有可能是动态编程问题?

3 个答案:

答案 0 :(得分:2)

基本上你希望能够在给定任意点数的mod的情况下计算任何数字的mod 7.

你可以做的是;

  • 记录时间和空间的每个点O(N)的模数。使用最多100 KB的内存。
  • 取两点的模数,并确定在开始之前减去数字的数量,例如O(N)时间和空间(一次不是每个循环)

e.g。介于2和3之间

357 % 7 = 0
3 % 7 = 3 and 300 % 7 = 6 (the distance between the start and end)

和0!= 6所以数字不是7的倍数。

介于4和4之间

3577 % 7 == 0
357 % 7 = 0 and 0 * 10 % 7 = 0

为0 == 0它是7的倍数。

答案 1 :(得分:2)

有两种方法可以解决这个问题。

1:动态规划方法
让输入为数字A[N]数组 让N[L,R]为数字L to R形成的数字 让另一个数组为M[N],其中M[i] = N[1,i] mod 7 所以M[i+1] = ((M[i] * 10) mod 7 + A[i+1] mod 7) mod 7
预先计算数组M

现在考虑表达。
N[1,R] = N[1,L-1] * 10 R-L + 1 + N[L,R]
 implies (N[1,R] mod 7) = (N[1,L-1] mod 7 *(10 R-L + 1 mod 7)) + (N[L,R] mod 7)
    implies N[L,R] mod 7 = (M[R] - M[L-1] *(10 R-L + 1 mod 7)) mod 7

N[L,R] mod 7给出了答案,可以在O(1)中计算,因为表达式右侧的所有值都已存在。 对于10 R-L + 1 mod 7,您可以为10的所有幂预先计算模7。

时间复杂性:
预先计算O(N)
总体O(Q) + O(N)

2:分而治之的方法
它是一个segment tree解决方案。 在每个树节点上,您将mod 7存储为该节点中数字形成的数字 并且第一种方法中给出的表达式可以用于通过组合两个孩子的mod 7值来找到父亲的mod 7 此解决方案的时间复杂度为O(Q log N) + O(N log N)

答案 2 :(得分:0)

首先为0开头的每个数字建立一个模数为7的数字列表(如你的情况,0%7,3%7,35%7,357%7 ...)然后为每个情况( a,b)抓取数字[a-1]和数字[b],然后将数字[b]乘以由(1+b-a)%6定义的10 ^ X模7的1-3-2-6-4-5序列,相比。如果这些相等,则返回YES,否则返回NO。伪代码:

readString(big);
Array a=[0]; // initial value
Array tens=[1,3,2,6,4,5]; // quick multiplier lookup table
int d=0;
int l=big.length;
for (int i=0;i<l;i++) {
    int c=((int)big[i])-48; // '0' -> 0, and "big" has characters
    d=(3*d+c)%7;
    a.push(d); // add to tail
}
readInt(q);
for (i=0;i<q;i++) {
    readInt(li);
    readInt(ri); // get question
    int left=(a[li-1]*tens[(1+ri-li)%6])%7;
    if (left==a[ri]) print("YES"); else print("NO");
}

测试示例:

247761901
1
5 9

61901%7 = 0。计算:

a = [0 2 3 2 6 3 3 4 5 2]
li = 5
ri = 9
left=(a[5-1]*tens[(1+9-5)%6])%7 = (6*5)%7 = 30%7 = 2
a[ri]=2
Answer: YES