[GoogleInterview] continuous subsequence problem

Before the Beginning

This article Clouder original article, the original link is https://www.codein.icu/gci-subarray/ , set at the beginning of this paragraph article reprint a conspicuous place. As was second creation, please clearly marked.

Foreword

In the decadent groundwater Telegram, to see someone in Codeforces group made a chart Zhang Gu Ge interview questions, or there are some meaning, some ran after harvest to God for help, write a solution to a problem.
The subject is not difficult, is how to solve the problem gracefully.

Face questions

GoogleInterviewProblem.png

Given an unordered array \ (A \) , length \ (N \) , the elements are all non-negative integers, it requires finding a contiguous sequence and such that it is \ (S \) .

Thinking

The idea of violence is very simple, about the enumeration endpoint fucks it wants. Complexity is probably \ (O (n ^ 3) \) is.
Little consideration of optimization, the pretreatment and the prefix, remain about enumeration endpoint complexity \ (O (^ n-2) \) .
This is the most intuitive idea, but the complexity of the requirements for the \ (O (the n-) \) , we must find a better algorithm.

Hash table method

既然有了前缀和,那么这一段子序列可以用数学语言来表示一下:
\(S = s_i - s_j(j \leq i)\)
其中 \(s\) 代表前缀和。
稍加变换,就可以变为:
\(s_i - S = s_j(j \leq i)\)
问题转化为是否存在 \(j \in [1,i]\) 使得 \(s_j = s_i - S\)
那么可以顺着扫一遍,判断之前是否有 \(s_j = s_i - S\),再将 \(s_i\) 的值记录下来。
伪代码:

for(int i = 1;i<=n;++i)
{
    s[i] = s[i - 1] + a[i];
    if(map[s[i] - S] != 0)
        return make_pair(map[s[i] - S] + 1,i);
    map[s[i]] = i;
}

那么复杂度的瓶颈就在于这个 map 如何实现了。使用红黑树可以做到稳定的 \(O(n\log n)\),而使用哈希表可以做到 \(O(n)\)
然而哈希表的复杂度相当玄学,并且在元素值域过大时表现并不好。
有没有更稳定的、优雅的解决方法呢?

双指针扫描法

这是与神犇讨论后产生的解法,笔者认为相当优雅,并且顺路膜拜了神犇。
双指针扫描发,或者说对撞指针法?网上的资料较少,我只能大致讲一下。
拿经典的两数之和来举例子吧。

首先保证数组有序,要求找到两个数和为定值。
那么初始化左指针为数组开头,右指针为数组末尾。
判断两数相加,若大于目标值,则右指针左移。若小于目标值,则左指针右移。
那么两个指针重合时终止。很容易证明复杂度为 \(O(n)\)

相信这个还是很容易理解的。
那么这道题,只是将两数之和变成了两数之差,也可以使用相类似的双指针法。
要求:
\(S = s_i - s_j(j \leq i)\)
先预处理出前缀和数组,由于元素都是非负整数,前缀和数组天然单调递增。
发现右指针右移时单调递增,左指针右移时单调递减,因此满足了单调性。
如果空数组也是可选的,那么右指针初始和左指针位置相同。
伪代码:

int lp = 0,rp = 0;
while(lp <= rp && lp >= 0 && rp <= n)
{
    if(s[rp] - s[lp] == S)
        return make_pair(lp + 1,rp);
    if(s[rp] - s[lp] < S)
        ++rp;
    else
        ++lp;
}

那么复杂度就是相当稳定的 \(O(n)\)了。

双指针扫描法证明

至于双指针法的正确性,感性理解很容易,但严谨证明,笔者觉得还是有些难度的。(当然是笔者太弱了)
在借鉴了 chend大佬的两数之和正确性证明 后,笔者也尝试自证一下。

使用数学归纳法证明算法运行过程中 \(\forall a \in [0,L],b \in [L+1,R]\)\(s_b - s_a \neq S\)

  1. 初始时,不考虑空数组的情况,从 \(L = 0,R = 1\) 开始,若成立则算法退出,否则命题成立。
  2. 假定 \(\forall a \in [0,L],b \in [L+1,R]\) 中命题已成立,
    欲证 \(\forall a \in [0,L+1],b \in [L+2,R]\) 中命题成立,
    \(s_{R} - s_{L+1} = S\) 则算法结束,因此要证明命题,
    即证 \(\forall b \in [L+2,R]\) 都有 \(s_b - s_{L+1} \neq S\)
    使用反证法证明,假定 \(\forall b \in [L+2,R]\)\(s_b - s_{L+1} = S\),若 \(b = R\) 则算法已结束,因此 \(b \in [L+2,R - 1]\)
    那么由单调性,有 \(s_b - s_{L} >= S\),且如果取等号则算法在先前已结束,因此 \(s_b - s_L > S\)
    根据定义,当 \(s_b - s_L > S\) 时,右指针将会固定在 \(b\) 的正确位置,左指针会直接移动到 \ (. 1 + L \) , and the right pointer does not reach the current \ (R & lt \) position, contradiction.
    Thus during operation algorithm, if \ (a \ in [0, L], b \ in [L + 1, R] \) of the proposition holds, the \ (a \ in [0, L + 1], b \ in [L + 2, R ] \) of the proposition holds.
  3. Similarly prove \ (a \ in [0, L], b \ in [L + 1, R + 1] \) of the proposition holds.

So in the course of running the algorithm, by definition move the pointer can always guarantee statement is true, do not miss \ - (s_b s_a = S \ ) situations.
As the author of the level of the problem, not strict proof, the reader can see the original big brother to prove themselves.

Epilogue

Do problems easily, gracefully pertinent difficult, more difficult to cut finish to permit ah ......
two gods to worship pointing the author of Ben expression of love.
Attach code package, comprising a data generator and the two methods, and testing of the metronome.
For convenience of implementation, a hash table is used instead of the container map.
Blue played cloud download

Guess you like

Origin www.cnblogs.com/Clouder-Blog/p/gci-subarray.html