细节注意: m i d = b e g + e n d − b e g 2 mid=beg+\frac{end-beg}{2} mid=beg+2end−beg,如此计算等价于 m i d = b e g + e n d 2 mid=\frac{beg+end}{2} mid=2beg+end,但后者可能导致溢出。
std::upper_bound
函数实现/* Returns an iterator pointing to the first element
* in the range [first, last) which compares greater than val.
*/
template <class ForwardIterator, class T>
ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, const T& val) {
ForwardIterator it;
iterator_traits<ForwardIterator>::difference_type count, step;
count = std::distance(first, last); // count is always be (last - first)
while (count > 0) {
it = first;
step = count / 2; // step = (last - first) / 2
std::advance(it, step); // it = first + (last - first) / 2
if (!(val < *it)) {
first = ++it; // choose the right range (it, last)
count -= step + 1;
}
else
count = step; // choose the left range [first, it)
}
return first;
}
lower_bound
函数实现/* Returns an iterator pointing to the first element
* in the range [first, last) which does not compare less than val.
*/
template <class ForwardIterator, class T>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T& val) {
ForwardIterator it;
iterator_traits<ForwardIterator>::difference_type count, step;
count = std::distance(first, last);
while (count > 0) {
it = first;
step = count / 2;
std::advance(it, step);
if (*it < val) {
first = ++it;
count -= step + 1;
}
else
count = step;
}
return first;
}
m i d = b e g + e n d 2 mid=\frac{beg + end}{2} mid=2beg+end 这一运算,当区间长度为奇数时,两种区间都是对应中间位置,当区间长度为偶数时,左闭右开区间对应中间偏右的位置,闭区间对应中间偏左的位置
左闭右开区间 | 闭区间 | 注 | |
---|---|---|---|
偶数长度区间偏左中值 | b e g + e n d − b e g − 1 2 beg+\frac{end-beg-1}{2} beg+2end−beg−1 | b e g + e n d − b e g 2 beg+\frac{end-beg}{2} beg+2end−beg | 前者当区间长度为奇数时会偏左 |
偶数长度区间偏右中值 | b e g + e n d − b e g 2 beg+\frac{end-beg}{2} beg+2end−beg | b e g + e n d − b e g + 1 2 beg+\frac{end-beg+1}{2} beg+2end−beg+1 | 后者当区间长度为奇数时会偏右 |
奇数长度区间中值 | b e g + e n d − b e g 2 beg+\frac{end-beg}{2} beg+2end−beg | b e g + e n d − b e g 2 beg+\frac{end-beg}{2} beg+2end−beg | - |
区间长度不为 0 | b e g < e n d beg |
b e g ≤ e n d beg\le end beg≤end | 前者若条件允许建议使用 ≠ \ne = |
区间长度不为 1 | b e g < e n d − 1 beg |
b e g < e n d beg |
- |
选取不含 mid 的左区间 | e n d = m i d end=mid end=mid | e n d = m i d − 1 end=mid-1 end=mid−1 | 后者请注意 m i d = 0 mid=0 mid=0 的情况 |
选取不含 mid 的右区间 | b e g = m i d + 1 beg=mid+1 beg=mid+1 | b e g = m i d + 1 beg=mid+1 beg=mid+1 | - |
细节注意:检查最后区间长度收敛到 1 时该元素与被查找元素的相对大小。
std::equal_range
闭区间版细节注意:先求的边界值可以用来初始化后求的边界值的搜索范围,C++ 的 std::equal_range
的实现也用了同种方法
本题可直接借鉴 std::upper_bound
的思想,搜索第一个平方值大于 x x x 的数,最后返回时将该数减 1 即可
本题亦有利用指数和对数的解法以及牛顿迭代法
二分查找即可,注意整数溢出
本题亦可使用库函数或使用牛顿迭代法辅助判断
std::unique
双指针法,可参考 C++ STL 源码
参照 27 双指针法
将原数组逐个计算其平方后可以得到一个 V 形数组,设置从两边开始的双指针,使用类似归并排序的 merge 方法对两个递减数组进行反向合并即可,注意每次取较大值放入结果数组,循环结束条件为两个指针相遇。
std::reverse
)使用双指针进行元素交换即可。
每隔 2k 个元素对当前块中的字符作对应的处理即可,注意边界条件。
" ".join(reverse(s.split()))
std::string::find
暴力破解,对 h a y s t a c k haystack haystack 中每一个属于 [ 0 , h a y s t a c k . l e n g t h − n e e d l e . l e n g t h ) [0,haystack.length-needle.length) [0,haystack.length−needle.length) 的下标进行检查
KMP 算法
前缀 p r e f i x prefix prefix:包含首字符的连续子串
后缀 s u f f i x suffix suffix:包含尾字符的连续子串
最长相等前后缀长度 n n n:对于字符串 s s s,求最大的 n n n,使得 s [ 0 , n ) = s [ s . l e n g t h − n , s . l e n g t h ) s[0,n) = s[s.length-n,s.length) s[0,n)=s[s.length−n,s.length),注意不是对称而是相等
n e x t next next 数组的计算方法:
class Solution {
public:
/**
* e.g. haystack = "abeababeabf"; needle = "abeabf";
* next = {-1, 0, 0, 1, 0, 0};
* The values for each time the "match" for-loop begin:
* strIdx: 0 1 2 3 4 5 6 7 8 9 10
* modIdx: -1 0 1 2 3 4 0 1 2 3 4
* when strIdx = 5 and modIdx = 4:
* 1. modIdx rollback to -1
* 2. ++modIdx
*/
int strStr(string haystack, string needle) {
int modLen = needle.size();
int strLen = haystack.size();
vector<int> next(modLen, 0);
next[0] = -1;
int prefixIdx = -1; // stand for the empty prefix
// calculate next[1:modLen]
for (int suffixIdx = 1; suffixIdx < modLen; ++suffixIdx) {
// rollback to the longest prefix for prefix + needle[prefixIdx + 1] == suffix
// prefixIdx may become -1
while (prefixIdx >= 0 && needle[suffixIdx] != needle[prefixIdx + 1]) {
prefixIdx = next[prefixIdx];
}
// the length of prefix is able to be appended
if (needle[suffixIdx] == needle[prefixIdx + 1]) {
++prefixIdx;
}
next[suffixIdx] = prefixIdx; // next[suffixIdx] maybe -1
}
// match
int modIdx = -1; // stand for the empty mod prefix
for (int strIdx = 0; strIdx < strLen; ++strIdx) {
// rollback to the longest modIdx for needle[0:modIdx + 2] == haystack[0:strIdx + 1]
// modIdx maybe -1
while (modIdx >= 0 && haystack[strIdx] != needle[modIdx + 1]) {
modIdx = next[modIdx];
}
// modIdx is able to be increased
if (haystack[strIdx] == needle[modIdx + 1]) {
++modIdx;
}
// success
if (modIdx == modLen - 1) {
return strIdx - modLen + 1;
}
}
return -1;
}
};
class Solution {
public:
bool repeatedSubstringPattern(string s) {
return (s + s).find(s, 1) != s.size();
}
};
维护一个滑动窗口,若其中所有数的和小于 x x x,则滑动右边界,否则滑动左边界,滑动时要刷新窗口中的数字和,记录滑动过程中的最小的窗口长度即可。须注意的细节是,若右边界已达到数组尾端而总和仍然小于 x x x 时可以直接停止计算
维护一个滑动窗口,用一个哈希表维护当前窗口中的每个元素的个数,视情况对数组进行滑动,记录最大的符合条件的窗口,须注意的细节是,若右边界已达到数组尾端可以直接停止计算
维护一个滑动窗口 [ b , e ) [b, e) [b,e)。建立两个哈希表 h s hs hs 和 h t ht ht,其中 h s hs hs 用于存储当前滑动窗口中每个字符出现的次数, h t ht ht 用于存储 t t t 中字符出现的次数。建立一个变量 c n t cnt cnt 表示当前滑动窗口对应的连续子串中已经匹配了多少个 t t t 中的字符,对于滑动窗口中的任意字符 c c c,最多匹配 h t ( c ) ht(c) ht(c) 次。
于是,我们在循环中每次将 e e e 右移,每次根据并入的字符更新 c n t cnt cnt,之后不断右移 b b b,直到当前右移操作会导致 h s ( s ( b ) ) < h t ( s ( b ) ) hs(s(b)) < ht(s(b)) hs(s(b))<ht(s(b)) 为止,若此时 c n t = s . l e n g t h cnt = s.length cnt=s.length,计算当前窗口长度,若当前窗口比原来的窗口更短(假定窗口长度初始为无穷大),则记录当前窗口。
转圈遍历,每转向一次便缩小范围,比如到达右上角准备向下遍历之前,把遍历上界自增 1 即可,注意细节
与上一题类似
可以先统计空格数量预先分配空间,也可以直接把 string
当作动态数组。