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)的复杂度实现呢?
这个栈从栈底到栈顶是由大到小的,如果有一个数破坏了这种单调性,就开始弹出,一旦弹出了就开始收集信息了。