📅  最后修改于: 2023-12-03 15:28:46.105000             🧑  作者: Mango
这是 GATE-IT-2004 考试中的一道计算题,涉及到程序设计中的概念和流程控制。
下面是问题的原文:
给定由 $n$ 个元素组成的数组 $A$,其中 $A_i \in {0,1}$。正整数 $k(n \geq k> 0)$ 去一个扫描窗口在 $A$ 上,窗口从左到右依次扫描,窗口宽度为 $k$,右端点为 $r$ ,左端点为 $l = r-k+1$。窗口的权重定义如下:$W(l) = \sum_{i=l}^{r}A_i*2^{i-l}$。你需要编写一个程序,对于给定的数组和正整数 $k$,输出所有满足 $W(l)$ 是 4 的倍数的窗口的左端点 $l$。
例如,对于输入数组 $A=[1,0,1,1,0,1,0,1,1]$ 和 $k=3$,我们可以得到如下的扫描窗口:
[1,0,1] 1 0 1 0 1 1
1 [0,1,1] 0 1 0 1
1 0 [1,1,0] 1 0
0 1 1 [0,1,1]
1 0 1 1 [0,1]
0 1 0 1 [1,1]
其中括号中的数字表示这个扫描窗口中的子数组,而其它数字则表示这个子数组中的每一个数,这些数在扫描窗口中依次排列。例如,在第一个扫描窗口中,$l=1$,$r=3$,而对应的子数组是 [1,0,1]。
对于每一个扫描窗口,我们都可以计算它的权重 $W(l)$,例如,对于第一个扫描窗口,$W(l)=12^0 + 02^1 + 1*2^2 = 5$。
问题要求我们找到所有满足 $W(l)$ 是 4 的倍数的窗口的左端点 $l$。在本例中,满足条件的左端点是 $l=4$ 和 $l=7$,因为对应的滑动窗口的权重分别为 $W(4)=42^0+12^1+12^2=12$ 和 $W(7)=12^0 + 12^1 + 12^2 + 1*2^3 = 14$,它们都是 4 的倍数。注意,这里仅仅要求输出所有满足条件的左端点,而不要求输出对应的右端点 $r$。
首先,我们需要明确一下题目的要求:对于一个固定的 $k$ 值,找到所有滑动窗口 $A[l,r], r=l+k-1$,使得 $W(l)=\sum_{i=l}^{r}A_i*2^{i-l}$ 是 4 的倍数,即 $W(l) \bmod 4 = 0$。为此,我们需要有一个算法,在 $O(n)$ 的时间内遍历所有可能的滑动窗口,并且对每一个窗口,都快速计算出它的权重。那么,怎样快速计算滑动窗口的权重呢?
首先,让我们来看一个稍微简单一些的问题:假设我们有一个 $n$ 位的二进制数 $x$,我们如何计算它的十进制值呢?如果我们从低到高考虑,那么 $x$ 的第 $i$ 位表示的数值为 $x_i 2^i$,所以 $x$ 的十进制值就是:
$$ V(x) = \sum_{i=0}^{n-1} x_i 2^i $$
可以看到,这样的计算需要执行 $O(n)$ 个乘法操作和 $O(n)$ 个加法操作,时间复杂度为 $O(n)$。这个计算过程实际上就是把 $x$ 看作是一个二进制多项式,然后把它转换成一个十进制整数。所以,我们的问题可以转化为:对于一个固定的 $k$ 值,怎样快速计算出由 $k$ 个二进制数构成的串的权重?
我们也可以采用类似的算法思路,考虑构造一个由 $k$ 个二进制数构成的多项式 $P(x)$,然后把它转换成一个十进制整数,这个整数就是这个由二进制数构成的串的权重。具体来说,我们设多项式为:
$$ P(x) = \sum_{i=0}^{k-1} A_{l+i} x^i $$
那么,根据权重的定义,我们可以得到:
$$ W(l) = V(P(2)) $$
其中 $V(x)$ 表示将多项式 $x$ 转换成十进制整数。因此,我们可以先计算出多项式 $P(x)$,然后再把它转换成十进制数。而对于计算多项式的方法,我们可以直接遍历所有二进制数 $A_{l+i}$,并且在每一步中计算出这个二进制数对应的 $x^i$ 的系数。具体来说,设第 $i$ 位二进制数为 $A_{l+i}$,那么 $P(x)$ 的系数 $a_i$ 就是:
$$ a_i = A_{l+i} 2^i $$
只需要累加所有这样的系数,就得到了 $P(x)$ 的系数。
这里有一个小问题:由于我们是按照从低位到高位的顺序遍历二进制数的,所以计算出的 $P(x)$ 实际上是 $A_{l+k-1} x^{k-1} + \cdots + A_{l+1} x + A_l$,而不是 $A_l x^{k-1} + \cdots + A_{l+k-2} x + A_{l+k-1}$。显然,它们是等价的,因为二进制数所代表的数值是不依赖于它的位数的排列顺序的。不过,由于题目要求的是 $W(l)$,因此我们必须让 $P(x)$ 的系数按照 $A_l, A_{l+1}, \cdots, A_{l+k-1}$ 的顺序排列。为了做到这点,我们可以对 $a_i$ 进行如下变换:
$$ \begin{aligned} a_i & = & A_{l+i} 2^i \ & = & A_{l+i} 2^{k-1} \cdot \dfrac{x}{2^k} \cdot 2^{i-k+1} \ & = & A_{l+i} \cdot x^i \cdot \dfrac{2^{k-i}}{2^k} \ & = & A_{l+i} \cdot x^i \cdot 2^{i-k} \end{aligned} $$
通过这个变换,我们可以用 $O(k)$ 的时间复杂度计算出多项式 $P(x)$ 了。而把多项式 $P(x)$ 转换成十进制数,只需要按照定义把系数相加就可以了,这个过程的时间复杂度也是 $O(k)$。
最后,我们可以编写一个伪代码,表示算法的整个过程。
首先,我们需要定义一个函数 calc_weight
,它的参数是数组 $A$、滑动窗口的左端点 $l$、窗口宽度 $k$,返回值是窗口的权重。
def calc_weight(A, l, k):
P = 0
for i in range(k):
P += (A[l+i] << i)
return P % 4 == 0
然后,我们需要遍历所有可能的滑动窗口,并且对每一个窗口,都调用一次 calc_weight
函数。如果返回值为 True
,那么说明这个窗口的权重是 4 的倍数,因此输出它的左端点。
def find_windows(A, k):
n = len(A)
for l in range(n-k+1):
if calc_weight(A, l, k):
print(l)
这就是整个算法的实现过程。
对于给定的 $k$ 值,算法的时间复杂度是 $O(nk)$,其中 $n$ 是数组 $A$ 的长度。因为 $k$ 是一个定值,所以算法可以认为是线性时间复杂度的。当然,由于计算多项式系数的过程中使用了位运算,因此实际上常数比较大,常常需要优化才能通过大数据测试。
这是一道比较简单的计算题,主要考察了对于位运算和多项式的理解和运用。具体来说,需要掌握如何计算二进制数的十进制值,以及如何用多项式表示由二进制数构成的串,并且计算它的十进制权重。此外,在算法设计方面,需要掌握如何利用滑动窗口遍历所有子串并进行判断。