C++ C++;字符串::查找复杂性

C++ C++;字符串::查找复杂性,c++,string,algorithm,substring,time-complexity,C++,String,Algorithm,Substring,Time Complexity,为什么c++实现的string::find()不使用(并且不在O(N+M)中运行)而在O(N*M)中运行?这在C++0x中正确吗? 如果当前查找的复杂性不是O(N*M),那是什么 那么gcc中实现了什么算法呢?那是国民党吗?若否,原因为何? 我已经对它进行了测试,运行时间显示它在O(N*M)中运行,您从哪里得到的印象是std::string::substr()没有使用线性算法?事实上,我甚至无法想象如何以一种具有您所引用的复杂性的方式实现。此外,这里没有太多的算法涉及:有没有可能你认为这个函数做

为什么c++实现的
string::find()
不使用(并且不在
O(N+M)
中运行)而在
O(N*M)
中运行?这在C++0x中正确吗? 如果当前查找的复杂性不是
O(N*M)
,那是什么

那么gcc中实现了什么算法呢?那是国民党吗?若否,原因为何?
我已经对它进行了测试,运行时间显示它在
O(N*M)

中运行,您从哪里得到的印象是
std::string::substr()
没有使用线性算法?事实上,我甚至无法想象如何以一种具有您所引用的复杂性的方式实现。此外,这里没有太多的算法涉及:有没有可能你认为这个函数做的不是它做的
std::string::substr()
只创建一个新字符串,从第一个参数开始,使用第二个参数指定的字符数或字符串末尾的字符数


您可能指的是没有任何复杂性要求的
std::string::find()
,或者确实允许进行O(n*m)比较的
std::search()
。然而,这是一个允许实现者在具有最佳理论复杂度的算法和不需要额外内存的算法之间进行选择的自由。由于除非指定请求,任意数量的内存分配通常是不可取的,这似乎是一个合理的事情。

< P> C++标准不指定<代码>子字符串< /代码>的性能特征。(或许多其他部分,包括
find
,您最有可能使用
M*N
复杂度来指代)

它主要规定语言的功能方面(例如,非遗留的
排序
函数除外)

实现甚至可以自由地将
qsort
实现为冒泡排序(但只有当它们想被嘲笑并可能倒闭时)

例如,在C++11的
21.4.7.2 basic_string::find
部分中,只有七个子点(非常小),并且没有一个子点指定性能参数

为什么c++实现的string::substr()不使用KMP算法(并且不在O(N+M)中运行)而在O(N*M)中运行

我假设您的意思是
find()
,而不是
substr()
,它不需要搜索,并且应该在线性时间内运行(而且只是因为它必须将结果复制到新字符串中)

<> P> C++标准没有指定实现细节,只在某些情况下指定了复杂度要求。对代码> STD的唯一复杂度要求是:String < /代码>操作,即:<代码>(代码)>,代码> Max()/代码>,<代码>操作符[]/Cube >,<代码> SWAP-()/代码>,<代码> CyString()> /Cord>和<代码>数据()
都是固定时间。其他任何事情的复杂性取决于实现您使用的库的人所做的选择

选择简单搜索而不是像KMP这样的搜索最可能的原因是为了避免需要额外的存储。除非要查找的字符串很长,并且要搜索的字符串包含大量的部分匹配,否则分配和释放所需的时间可能远远超过额外复杂性的成本

这在c++0x中正确吗

不,C++11没有向
std::string
添加任何复杂性要求,当然也没有添加任何强制性的实现细节

如果当前substr的复杂度不是O(N*M),那是什么


当要搜索的字符串包含大量长部分匹配时,这是最糟糕的复杂度。如果字符具有合理的均匀分布,则平均复杂度将更接近
O(N)因此,通过选择一个具有更好的最坏情况复杂性的算法,你可以使更多的典型情况慢得多。

你从哪里得到关于C++库的信息?如果你的意思是“代码>字符串::搜索< /代码>,它确实不使用KMP算法,那么我建议这是因为该算法一般不快。一个简单的线性搜索,因为必须先建立部分匹配表,然后才能继续搜索。

FYI, gcc/libstdc++和llvm/libcxx中的字符串::find都非常慢。我对它们都做了相当大的改进(在某些情况下提高了约20倍)。您可能需要检查新的实现:

GCC:PR66414优化标准::字符串::查找

LLVM:


新算法更简单,并使用手动优化的memchr和memcmp组装函数。

让我们看看CLRS手册。在第三版的989页上,我们有以下练习:

假设模式p和文本T是随机选择的字符串 d进制字母表中的长度分别为m和n Ʃd={0;1;…;d},其中d>=2。显示 所做的字符对字符比较的预期数量 naive算法第4行中的隐式循环是
在该循环的所有执行过程中。(假设naive算法 一旦发现不匹配,将停止比较给定移位的字符 因此,对于随机选择的字符串 naive算法非常有效

证明:


对于单个移位,我们需要执行
1+1/d+…+1/d^{m-1}
比较。现在使用求和公式并乘以有效移位数,即
n-m+1
。□

如果要在多个文本中搜索同一个模式。BoyerMoore算法是一个不错的选择,因为模式表只需计算一次,但在搜索多个文本时会使用多次。但是,如果只在一个文本中搜索一次模式,则计算表的开销将随着分配内存的开销使您的速度过慢,std::string.fin
NAIVE-STRING-MATCHER(T,P)
1 n = T:length
2 m = P:length
3 for s = 0 to n - m
4     if P[1..m] == T[s+1..s+m]
5         print “Pattern occurs with shift” s