题目描述
一个长度为 n 的数字 arr,该数组中除了下标为 p 的位置为 1,其他位置均为 0。
一个 banned 数组,它的内容表示 arr 数组中的位置,也就是满足所有的 arr[banned[i]]=0,其中 banned[i]!=p。
返回一个大小为 n 的数组 ans,其中 ans[i] 表示:数组 arr 经过多少次翻转可以让 i 位置出现 1。
如果不能实现,则 ans[i]=-1。
'翻转大小为 k 的子数组,是指将该子数组逆序。'
思路分析
- 首先知道 arr 数组中 p 位置的值为 1,即 arr[p]=1,其余位置为 0。目标是翻转大小为 k 的子数组,使得其他位置也出现 1,求每个位置的翻转次数。
那么 ans[p]=0,因为 p 位置不用翻转,本来就是 1,所以翻转次数为 0。
假设现在 i 位置的值为 1。假设下标 i 经过一次翻转后的下标为 j,这个下标 j 肯定不是一个特定的下标,它代表翻转后的所有可能的下标。那么我们可以将 i 和 j 看成用一条边连接,这条边的边权为 1,表示从 i 位置翻转到 j 位置的翻转次数。可以理解为求最短路径的过程。
即已经知道了 ans[i] 的值,arr[i]=1。i 位置经过一次翻转后的下标为 j,将 i 和 j 看成是用一条边连接,这条边的边权为 1,代表翻转次数。那么就可以得到 ans[j]=ans[i]+1。下次再从 j 位置开始扩展。
可以看出这是一个BFS的过程。下面的问题是怎么求 i 翻转后的下标 j???
- 对于一个 子数组【L,R】
我们对于这整个区间翻转的话,可以得到:
下标 L 翻转后的下标为 R。
下标 L+1 翻转后的下标为 R-1。
下标 L+2 翻转后的下标为 R-2。
下标 L+3 翻转后的下标为 R-3。
..............
下标 R 翻转后的下标为 L。
可以得出,翻转前后的下标之和始终为 L+R。所以对于下标 i,翻转后的下标 j=L+R-i。
再者,对于区间【L,R】,可以左移,L+1,R+1。也可以右移 L-1,R-1。
而对于翻转后的下标 j=L+R-i 而言,就是 +2 或者 -2。
所以翻转后的下标 j=L+R-i,其实是一个公差为 2 的等差数列,要么都是偶数,要么都是奇数。
- 下标 i 翻转后的下标 j 的范围。
对于下标 i,当它在子数组的最右侧时,可以得到翻转后的下标 j=i-k+1,为最小值。
对于下标 i,当它在子数组的最左侧时,可以得到翻转后的下标 j=i+k-1,为最大值。
还有一些特殊情况:
当 i<k-1 时,i 不可能在子数组的右端点。当 i>n-k 时,i 不可能在子数组的左端点。
例如 对于 k=4,长度为 4 的子数组,右端点最小是 k-1=3,当 i=0,1,2,i 不可能是数组的右端点;左端点最大是 n-k,当 i>n-k,i 不可能在子数组的左端点。
i<k-1 的情况,当子数组在整个数组的最左边时,L=0,R=k-1。i 翻转后的下标 j=L+R-1=k-1-i。小于 k-i-1 的下标无法翻转得到。
i>n-k 的情况,当子数组在整个数组的最右边时,L=n-k,R=n-1。翻转后的下标为 j=L+R-i=2n-k-i-1。大于 2n-k-i-1 的下标无法翻转得到。
综上所述:
i 翻转后的最小值为 max(i-k+1,k-1-i)。
i 翻转后的最大值为 min(i+k-1,2*n-k-i-1)。
算法实现
BFS+ 有序集合
由于翻转后的下标是一个公差为 2 的等差数列,要么都是奇数,要么都是偶数。
我们可以用两个有序集合来维护没有访问过的奇数下标和偶数下标。
这些下标不能在 banned 数组中出现过,banned 数组是限制数组。
还有下标 p 也不需要出现在集合中,因为刚开始初始化 ans[p]=0,已经访问过了。


