假设我有一个字符串,其字符只是[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?
答案 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