严正声明:本文系作者davidhopper原创,未经许可,不得转载。
std::search
算法原理std::search
算法定义于头文件
中,不考虑C++ 17
标准中提出的执行策略,也不考虑使用自定义的二元谓词(binary predicate
,即接受两个参数,返回值类型为bool
的函数或仿函数,形如:bool pred(const Type1 &a, const Type2 &b);
,a
和b
相等时,返回true
),函数模板定义如下:
template< class ForwardIt1, class ForwardIt2 >
ForwardIt1 search( ForwardIt1 first, ForwardIt1 last,
ForwardIt2 s_first, ForwardIt2 s_last );
更为详细的函数模板定义参考std::search网页。
上述函数模板的含义是:在前向迭代器(std::string
、std::vector
、std::list
、std::deque
、std::set
、std::map
等常见容器均满足该条件)容器区间[first, last)
(左闭右开,即first
是开始位置,last
是结束位置的下一个位置)中,寻找子序列[s_first, s_last)
首次出现的位置(实际寻找范围为[first, last - (s_last - s_first))
),并将该位置以迭代器的形式返回。若找不到这种子序列,则返回 last
;若[s_first, s_last)
为空,则返回first
。算法的时间复杂度为S*N
(最坏情形),其中 S = std::distance(s_first, s_last)
,而N = std::distance(first, last)
。
可能的实现代码如下:
template <class ForwardIt1, class ForwardIt2>
ForwardIt1 search(ForwardIt1 first, ForwardIt1 last, ForwardIt2 s_first,
ForwardIt2 s_last) {
std::size_t s_num = std::distance(s_first, s_last);
for (;; ++first) {
if (std::distance(first, last) < s_num) {
return last;
}
ForwardIt1 it = first;
for (ForwardIt2 s_it = s_first;; ++it, ++s_it) {
if (s_it == s_last) {
return first;
}
if (it == last) {
return last;
}
if (!(*it == *s_it)) {
break;
}
}
}
}
实现代码非常简洁,下面以图解方式进行解释。
先看第一幅图:
步骤一:
如(a)
所示,首先将first
指向容器区间[first, last)
的首个位置,即容器区间范围为[first, last)
,若容器区间[first, last)
的长度小于std::distance(s_first, s_last)
,往后搜索已无法满足遍历子序列[s_first, s_last)
的要求,此时表明在容器区间[first, last)
中找不到匹配的子序列[s_first, s_last)
,返回last
。
将it
指向first
,s_it
指向s_first
。若子序列[s_first, s_last)
为空,即s_it == s_last
,则直接返回first
。
比较*it
与*s_it
,若二者相等,则执行(b)
,否则表明待搜索位置s_it
与容器位置it
的值不匹配,立即中断子序列[s_first, s_last)
范围内的搜索迭代。
如(b)
所示,将it
和s_it
均向前移一步,比较*it
与*s_it
,若二者相等,则继续执行(b)
,否则表明待搜索位置s_it
与容器位置it
的值不匹配,立即中断子序列[s_first, s_last)
范围内的搜索迭代。
不断执行(b)
,直到如(c)
所示,s_it
抵达子序列[s_first, s_last)
的终点s_last
。此时表明子序列已遍历比较完毕,成功匹配,于是返回first
即可。或者,s_it
尚未抵达终点s_last
,但it
已抵达终点last
,此时表明在容器区间[first, last)
中找不到匹配的子序列[s_first, s_last)
,返回last
。
在执行了:
if (std::distance(first, last) < s_num) {
return last;
}
语句后,上述加粗的条件理论上不会触及,但这里仍写出以作为安全冗余设计,下同,不再赘述。
接下图看第二幅图:
步骤二:
如(d)
所示,将first
向前移一步,即容器区间范围调整为[first_old+1, last)
,将it
指向first
,s_it
指向s_first
,比较*it
与*s_it
,若二者相等,则执行( e)
,否则表明待搜索位置s_it
与容器位置it
的值不匹配,立即中断子序列[s_first, s_last)
范围内的搜索迭代。
如(e)
所示,将it
和s_it
均向前移一步,比较*it
与*s_it
,若二者相等,则继续执行(e)
,否则表明待搜索位置s_it
与容器位置it
的值不匹配,立即中断子序列[s_first, s_last)
范围内的搜索迭代。
不断执行(e)
,直到如(f)
所示,s_it
抵达子序列[s_first, s_last)
的终点s_last
。此时表明子序列已遍历比较完毕,成功匹配,于是返回first
即可。或者,s_it
尚未抵达终点s_last
,但it
已抵达终点last
,此时表明在容器区间[first_old+1, last)
中找不到匹配的子序列[s_first, s_last)
,返回last
。
步骤三:
如(g)
所示,不断将first
向前移一步,即容器区间范围调整为[first_old+n, last)
,将it
指向first
,s_it
指向s_first
,比较*it
与*s_it
,若二者相等,则执行(h)
,否则表明待搜索位置s_it
与容器位置it
的值不匹配,立即中断子序列[s_first, s_last)
范围内的搜索迭代。
如(h)
所示,将it
和s_it
均向前移一步,比较*it
与*s_it
,若二者相等,则继续执行(h)
,否则表明待搜索位置s_it
与容器位置it
的值不匹配,立即中断子序列[s_first, s_last)
范围内的搜索迭代。
不断执行(h)
,直到如(i)
所示,s_it
抵达子序列[s_first, s_last)
的终点s_last
。此时表明子序列已遍历比较完毕,成功匹配,于是返回first
即可。或者,s_it
尚未抵达终点s_last
,但it
已抵达终点last
,此时表明在容器区间[first_old+n, last)
中找不到匹配的子序列[s_first, s_last)
,返回last
。
整个搜索过程对照示意图就非常容易理解。
std::search
算法示例示例参考std::search网页,如下所示:
#include
#include
#include
#include
#include
template <typename Container>
bool in_quote(const Container& cont, const std::string& s) {
return std::search(cont.begin(), cont.end(), s.begin(), s.end()) !=
cont.end();
}
int main() {
std::string str = "why waste time learning, when ignorance is instantaneous?";
// str.find() can be used as well
std::cout << std::boolalpha << in_quote(str, "learning") << std::endl
<< in_quote(str, "lemming") << std::endl;
std::vector<char> vec(str.begin(), str.end());
std::cout << std::boolalpha << in_quote(vec, "learning") << std::endl
<< in_quote(vec, "lemming") << std::endl;
return 0;
}
输出结果为:
true
false
true
false