数字总和可被6整除的子序列

时间:2015-07-08 03:32:21

标签: string algorithm optimization dynamic-programming subsequence

假设我有一个字符串,其字符只是[0 - 9]范围内的数字。例如:“2486”。现在我想找出其数字总和可被6整除的所有子序列。例如:在“2486”中,子序列为 - “6”,“246”(2 + 4 + 6 = 12可被6整除), “486”(4 + 8 + 6 = 18可以被6整除)等等。我知道生成所有2 ^ n个组合我们可以做到这一点。但这非常昂贵。最有效的方法是什么?

编辑:

我在quora中找到了以下解决方案。

int len,ar[MAXLEN],dp[MAXLEN][MAXN];

int fun(int idx,int m)

{

    if(idx==len)

        return (m==0);

    if(dp[idx][m]!=-1)

        return dp[idx][m];

    int ans=fun(idx+1,m);

    ans+=fun(idx+1,(m*10+ar[idx])%n);

    return dp[idx][m]=ans;

}

int main()

{

    // input len , n , array

    memset(dp,-1,sizeof(dp));

    printf("%d\n",fun(0,0));            

    return 0;

}

有人可以解释一下代码背后的逻辑 - 'm * 10 + ar [idx])%n'?为什么m在这里乘以10?

3 个答案:

答案 0 :(得分:1)

假设您有一个16位数的序列您可以生成所有2个 16 子序列并测试它们,这是65536次操作。

或者您可以取前8个数字并生成2 8 个可能的子序列,并根据它们的和模6的结果对它们进行排序,并对最后8个数字执行相同的操作。这只是512次操作。

然后,您可以通过将第一个列表的每个子序列的模数值等于0(包括空子序列)并将其与最后一个子序列连接,生成可被6整除的原始16位数字符串的所有子序列。模数值等于0的列表。

然后取第一个列表的每个子序列,其模数值等于1,并将其与最后一个列表的每个子序列连接,模数值等于5.然后2表示4,3表示3,4表示2和5用1。

因此,在512次操作的初始成本之后,您只能生成其总和可被6整除的子序列。您可以递归地应用此算法以获得更大的序列。

答案 1 :(得分:0)

为字符串中的每个位置创建一个带有6位位图的数组。从右到左工作并设置位图数组,以便当有一些子序列从位于位图中的该位置的数组之后开始时,位图在数组中设置了位。您可以使用位于当前位置之后的位图从右到左执行此操作。如果你看到3和当前位置之后的位图是010001,那么只需跳过3就可以访问总和1和5.使用3和4和2现在可用,所以新位图是011011。

现在从左到右进行深度优先搜索子序列,每个字符的选择是要么采用该字符。当你这样做时,记录到目前为止所采用的字符的mod 6总和。使用位图来确定该位置的右边是否有一个子序列,到目前为止的总和为零。只要你看到当前的总和导致零和的后续序列,就继续进行,否则停止并递归。

第一阶段的输入大小成本是线性的(固定值为6)。第二阶段的成本与所产生的子序列成比例成线性关系。事实上,如果你必须实际写出所访问的子序列(例如,通过维护显式堆栈并写出堆栈的内容),这将是该程序中最昂贵的部分。

当所有2 ^ n个子序列都有效时,最坏的情况当然是输入000000 ... 0000。

答案 2 :(得分:0)

我非常确定一个名为amit的用户最近回答了一个类似的问题,即除数是4的组合而不是子序列,尽管我现在无法找到它。他的答案是在Array_i中创建五个数组(称为O(n)),其中每个数组包含具有模块化关系i的数组元素,其中包含6个子序列。需要一种方法来记录元素顺序。例如,在2486的情况下,我们的数组可能是:

Array_0 = [null,null,null,6]
Array_1 = []
Array_2 = [null,4,null,null]
Array_3 = []
Array_4 = [2,null,8,null]
Array_5 = []

现在只需交叉组合相应的数组,维护元素顺序:Array_0,Array_2& Array_4,Array_0&任何其他数组组合:

6, 24, 48, 246, 486