algorithm - 给定数组中可被 n 整除的子序列数

标签 algorithm

我有一个数字序列,例如 1、2、4、0,我必须找到可被 6 整除的序列数。

所以我们将有 0,12,24,120,240,这意味着答案将为 5,

问题是我设计了一个算法,它需要 O(2^n) 时间复杂度,所以基本上它会遍历所有天真的可能性。

有什么方法可以降低复杂性。

Edit1:允许复制多个数字。例如输入可以是 1,2,1,4,3 Edit2:数字应该按顺序排列,例如上面的示例 42 420 等是不允许的

代码:然而,此代码无法考虑 120

`#include <stdio.h>
 #include<string.h>
 #define m 1000000007
 int main(void) {
 int t;
 scanf("%d",&t);
 while(t--)
 {
    char arr[100000];
    int r=0,count=0,i,j,k;
    scanf("%s",&arr);
    int a[100000];
    for(i=0;i<strlen(arr);i++)
    {
        a[i]=arr[i]-'0';
    }
    for(i=0;i<strlen(arr);i++)
    {
        for(j=i;j<strlen(arr);j++)
        {
            if(a[i]==0)
            {
                count++;
                goto label;
            }
            r=a[i]%6;
            for(k=j+1;k<strlen(arr);k++)
            {
                r=(r*10 + a[k])%6;
                if(r==0)
                count++;
            }
        }
        label:;
        r=0;
    }
    printf("%d\n",count);
 }
 return 0;
}

最佳答案

你可以使用动态规划。

像往常一样,当我们决定使用动态规划来解决问题时,我们首先将一些输入值转换为参数,然后可能会添加一些其他参数。

参数的明显候选者是序列的长度。 让我们的序列为 a[1], a[2], ..., a[N] . 因此,我们搜索值 f(n)(对于从 0Nn),它是a[1]a[2]...a[n] 的子序列数当读作数字时,它可以被 D=6 整除。 当我们知道 f(n-1) 时计算 f(n) 看起来还不明显,所以我们深入研究细节。

仔细看,我们现在面临的问题是,在一个数的末尾加一个数字,可以把一个可以被D整除的数变成一个不能被D整除的数>,反之亦然。 不过,我们确切地知道当我们在数字末尾添加一个数字时余数如何变化。

如果我们有一个序列 p[1], p[2], ..., p[k] 并知道 r,余数 p[1] p[2] ... p[k]D,然后将p[k+1]加入序列,新数p[1] p[2]的余数s ... p[k] p[k+1] modulo D 很容易计算:s = (r * 10 + p[k+1]) mod D.

考虑到这一点,我们可以将余数取模 D 作为我们的新参数。 所以,我们现在搜索 f(n,r)(对于 n0Nr from 0 to D-1) 是a[1]的子序列数,a [2], ..., a[n] 当读作数字时,余数 rD.

现在,知道 f(n,0), f(n,1), ..., f( n,D-1),我们要计算f(n+1,0), f(n+1,1), ...f(n+1,D-1)。 对于 a[1]a[2]...a[n] 的每个可能子序列>,当我们考虑元素编号 n+1 时,我们要么向其添加 a[n+1],要么省略 a[n+1] 并保持子序列不变。 这比用公式更容易用正向动态规划来表达:

let f (n + 1, *) = 0
for r = 0, 1, ..., D - 1:
    add f (n, r) to f (n + 1, r * 10 + a[n + 1])  //   add a[n + 1]
    add f (n, r) to f (n + 1, r)                  //  omit a[n + 1]

结果 f (n + 1, s)(取决于 s,是一个或多个项的总和)是 a[1], a[2], ..., a[n], a [n+1] 产生余数 sD

整个解决方案如下:

let f (0, *) = 0
let f (0, 0) = 1  //  there is one empty sequence, and its remainder is 0
for n = 0, 1, ..., N - 1:
    let f (n + 1, *) = 0
    for r = 0, 1, ..., D - 1:
        add f (n, r) to f (n + 1, r * 10 + a[n + 1])  //   add a[n + 1]
        add f (n, r) to f (n + 1, r)                  //  omit a[n + 1]
answer = f (N, 0) - 1

我们从答案中减去一个,因为空子序列不被视为数字。

时间和内存要求是O (N * D)。 当我们注意到在每个给定时刻,我们只需要存储 f (n, *)f 时,我们可以将内存降低到 O (D) (n + 1, *),所以f的存储可以是2 * D而不是(N + 1) * D.

示例序列的插图:

-------------------------------
  a[n]           1   2   4   0
 f(n,r)  n   0   1   2   3   4
    r
-------------------------------
    0        1   1   2   3   6
    1        0   1   1   1   1
    2        0   0   1   2   4
    3        0   0   0   0   0
    4        0   0   0   2   5
    5        0   0   0   0   0
-------------------------------

练习:如何使用此解决方案去除带前导零的数字? 我们需要另一个参数吗?

关于algorithm - 给定数组中可被 n 整除的子序列数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31086991/

相关文章:

algorithm - 查找配对排序的有效方法?

sql - 计算两个日期之间缺失的日期范围和重叠的日期范围

algorithm - 在比线性时间更快的时间内找到满足属性的范围

algorithm - 是否有用于递增生成希尔伯特点曲线的恒定时间算法?

javascript - Javascript遗留的噩梦-试图绕过一些压缩的JavaScript

algorithm - 区间(图论)算法讲解

algorithm - Alpha Beta 剪枝,alpha 等于或大于 beta。为什么等于?

java - 如何为 N-Queen Hill Climbing 生成邻居

algorithm - 均匀分布矩形

PHP 比较数组元素作为一个整体