KMP && Manacher

KMP 解决的是包含的问题, 原始问题 string1中是否包含string 2,如果包含请返回在string1中问题。

vector<int> getNext(const string& s)
{
    // 判断s的长度
    if(s.size()==1)  return vector<int>{-1};
    int len = s.size();
    vector<int> next(len, -1);
    next[1] = 0;
    // 当前字符串的指向
    int i=2;
    int cn=0; // 应该进行对比的位置
    while(i < (int)s.size())
    {
        if(s[i-1] == s[cn])
        {
            next[i++]=++cn;
        }
        else if(cn > 0)
        {
            cn = next[cn];
        }
        else
        {
            next[i++] = 0;
        }
    }
    return next;
}

// 返回的是s2 在s1中出现的下标位置,如果没有出现则返回-1
int KMP(string const s1, string const s2)
{
    int i1 = 0;
    int i2 = 0;
    vector<int> next = getNext(s2);
    while(i1 < (int)s1.size() && i2 < (int)s2.size())
    {
        if(s1[i1] == s2[i2])
        {
            i1++;
            i2++;
        }
        else
        if(i2==0)// i2==0 等价的应该是。
        {
            i1++;
        }
        else
        {
            i2 = next[i2];
        }
    }
    return i2==(int)s2.size()? i1-i2 : -1;
}
// manacher算法 生成带有分割字符的数组
vector<char> manacherString(string s) { vector<char> arr(s.size()*2+1, '#'); for(int i=0; i<s.size(); ++i) { arr[2*i+1] = s[i]; } return arr; } int manacher(string s) { if(s.size()==0) return 0; if(s.size()==1) return 1; vector<char> charArr = manacherString(s); vector<int> pArr(charArr.size(), 0); int C = -1; int R = -1; int max_ = INT_MIN; for(int i=0; i<charArr.size(); ++i) { pArr[i] = R > i ? min(pArr[2*C-i], R-i):1; while(i+pArr[i]<charArr.size() && i-pArr[i]>=0) { if(charArr[i+pArr[i]] == charArr[i-pArr[i]]) { pArr[i]++; } else { break; } } if(i+pArr[i] > R) { R = i+pArr[i]; C = i; } max_ = max(max_, pArr[i]); } for(auto ele: pArr) cout << ele << " "; cout << endl; return max_-1; }
// 如果数组里面后偶数,就不用看了,因为那是分隔符的回文串

在一个无序数组中找到第K小的数,或者是在一个无序数组中找到第K大的数。

在一个数组中选择一个数做划分值,按照荷兰国旗的方式。第K小的数是从1开始的。然后判断在那个部分,是小于部分还是大于部分或者是中间部分。

这个算法虽然可以解决问题,但是取决于具体的划分情况,如果划分的不是均衡,就会出问题。

BFTRT 如何选取划分值的呢?

1  相邻的5个数一组,不满5个的,单独成为一组。

2  每个小组排个序 跨组之间不排序 这个排序时间是O(1)的,整体时间复杂度是O(N)。

3 每个组的中位数拿出来,构成一个新数组,如果是奇数,有唯一的中位数,如果是偶数,就选择上中位数。这个新的数组长度是n/5的。

4,递归调用BFPRT算法,将这个新的数组传进去,还有这个新数组的长度的一半,目的就是找寻这个新数组中的中位数。

当这个返回值拿到了,就拿到这个返回值进行划分,剩下的就是荷兰国旗问题了。

为什么要选这个值?这样就可以确定最差的可能性

总结概括一下

逻辑分组 o(1)
组内排序o(1)

所有数选出中位数 o(n)

bfprt调用自己 选择中位数, o(n/5)

划分值 o(N)

如果没有命中 左右两边直走一侧,此时,左部分最差O(7/10*N),右部分最多是O(7/10*N)

因此 时间复杂度就是将这些加起来,时间复杂度为O(N)。

应用,在一个数组中找到最大的K个数,用堆做的话就是O(NlogK),最优解是BFPRT算法。

窗口内最大值数组  使用双端队列的结构

vector<int> getMaxArr(vector<int> arr, int w)
{
    //用来存储当前最大值的数据结构 双端队列
    deque<int> maxq;
    // 返回的值
    vector<int> res(arr.size()-w+1, 0);
    int idx = 0;
    for(int i=0; i<arr.size(); ++i)
    {
        //增加逻辑
        while(!maxq.empty() && arr[maxq.back()] <= arr[i])
        {
            maxq.pop_back();
        }
        maxq.push_back(i);

        // 判断头节点是否已经超期了 也就是删除逻辑
        if(maxq.front() == i-w)
        {
            maxq.pop_front();
        }
        //如果窗口已经形成
        if(i>=w-1)
        {
            res[idx++] = arr[maxq.front()];
        }
    }
    return res;
}

求数组中有多少个子数组小于等于num值

int getNum(vector<int> arr, int num)
{
    if(arr.size()==0) return 0;
    deque<int> qmin;
    deque<int> qmax;
    int i=0;
    int j=0;
    int res = 0;
    while(i<arr.size())
    {
        while(j<arr.size())
        {
            while(!qmin.empty() && arr[qmin.back()] >= arr[j])
            {
                qmin.pop_back();
            }
            qmin.push_back(j);

            while(!qmax.empty() && arr[qmax.back()] <= arr[j])
            {
                qmax.pop_back();
            }
            qmax.push_back(j);

            if(arr[qmax.front()] -  arr[qmin.front()] > num)
            {
                break;
            }
            j++;
        }
        if(qmin.front() == i)
        {
            qmin.pop_front();
        }
        if(qmax.front() == i)
        {
            qmax.front();
        }
        res += j-i;
        //更新
        i++;
    }
    return res;
}

单调栈

在一个数组中,左边比它大的,和右边比它大的,最近的数是什么?

暴力的方法就是左右同时进行,也就是O(N^2)的复杂度,能不能用O(N)的复杂度实现呢?

这个栈从栈底到栈顶是由大到小的,如果有一个数破坏了这种单调性,就开始弹出,一旦弹出了就开始收集信息了。

猜你喜欢

转载自www.cnblogs.com/randyniu/p/9288314.html