【问题标题】:How do I find all the positions of a substring in a string?如何找到字符串中子字符串的所有位置?
【发布时间】:2011-04-28 08:33:59
【问题描述】:

我想在一个大字符串中搜索一个字符串的所有位置。

【问题讨论】:

    标签: c++ string find


    【解决方案1】:

    另外两个答案是正确的,但它们非常慢并且具有 O(N^2) 复杂度。但是有 Knuth-Morris-Pratt 算法,它可以找到 O(N) 复杂度的所有子字符串。

    编辑:

    此外,还有另一种算法:所谓的“Z 函数”,复杂度为 O(N),但我找不到该算法的英文来源(可能是因为还有另一个更著名的算法) name - Rieman 的 Z 函数),所以我将把它的代码放在这里并解释它的作用。

    void calc_z (string &s, vector<int> & z)
    {
        int len = s.size();
        z.resize (len);
    
        int l = 0, r = 0;
        for (int i=1; i<len; ++i)
            if (z[i-l]+i <= r)
                z[i] = z[i-l];
            else
            {
                l = i;
                if (i > r) r = i;
                for (z[i] = r-i; r<len; ++r, ++z[i])
                    if (s[r] != s[z[i]])
                        break;
                --r;
            }
    }
    
    int main()
    {
        string main_string = "some string where we want to find substring or sub of string or just sub";
        string substring = "sub";
        string working_string = substring + main_string;
        vector<int> z;
        calc_z(working_string, z);
    
        //after this z[i] is maximal length of prefix of working_string
        //which is equal to string which starting from i-th position of
        //working_string. So the positions where z[i] >= substring.size()
        //are positions of substrings.
    
        for(int i = substring.size(); i < working_string.size(); ++i)
            if(z[i] >=substring.size())
                cout << i - substring.size() << endl; //to get position in main_string
    }
    

    【讨论】:

    • hmm,你不觉得std::string::find的作者可能在下面实现了KMP吗?事实上,随着新的 SSE4.2 指令的出现,这种类型的优化可能已经在低级函数中可用(例如 glibc 中的strstr)——除非您在自己的代码中使用这些指令,否则高度您不太可能击败那些低级功能的性能。最终结果,除非你能凭经验证明它真的很慢,否则不要重新发明...... m2c
    • 你可以自己测试一下。让我们考虑 main_string = "a"(1000000 次)+"b"+"a"(1000000 次)。和 substring = "a"(999999 次)。使用 std::string::find 和@Kiril Kirov 的代码,您的程序将运行 2-3 天,但我的程序将立即返回。
    • 您可能还想考虑使用Boyer-Moore 算法,它是 O(N) 并且用更长的针(子串)表现更好。
    • 当确实有两个参数决定运行时,N_haystack 和 N_needle 时,将字符串查找算法描述为 O(N) 或 O(N*N) 有点荒谬。当 N_needle = 1 时,几乎所有算法都是 O(N_haystack)。大多数是 O(N_haystack * N_needle) 或更好,您可以假设 N_needle
    【解决方案2】:

    使用std::string::find。您可以执行以下操作:

    std::string::size_type start_pos = 0;
    while( std::string::npos != 
              ( start_pos = mystring.find( my_sub_string, start_pos ) ) )
    {
        // do something with start_pos or store it in a container
        ++start_pos;
    }
    

    编辑:Doh!谢谢你的评论,纳瓦兹!更好?

    【讨论】:

    • 这样更好。但是现在,你需要循环内的if-else 吗?
    • 哈,不错的 :) 当我还在半睡半醒时会发生这种情况 :D 不,我不需要它。第一次,我以为没有它,我可能会错过最后一个字符,如果my_sub_string只是一个字母,那就放在mystring的末尾。再次感谢!
    • 看起来比以前好多了...+1
    【解决方案3】:

    为了完整起见,我补充一下,std::search 可以使用另一种方法,类似于std::string::find,不同之处在于您使用迭代器,例如:

    std::string::iterator it(str.begin()), end(str.end());
    std::string::iterator s_it(search_str.begin()), s_end(search_str.end());
    
    it = std::search(it, end, s_it, s_end);
    
    while(it != end)
    {
      // do something with this position..
    
      // a tiny optimisation could be to buffer the result of the std::distance - heyho..
      it = std::search(std::advance(it, std::distance(s_it, s_end)), end, s_it, s_end);
    }
    

    我发现这个有时优于std::string::find,尤其是。如果您将字符串表示为vector&lt;char&gt;。

    【讨论】:

      【解决方案4】:

      只需use std::string::find(),它返回找到子字符串的位置,如果没有找到,则返回std::string::npos。

      Here 是文档。

      这里是取自本文档的示例:

      // string::find
      #include <iostream>
      #include <string>
      using namespace std;
      
      int main ()
      {
        string str ("There are two needles in this haystack with needles.");
        string str2 ("needle");
        size_t found;
      
        // different member versions of find in the same order as above:
        found=str.find(str2);
        if (found!=string::npos)
          cout << "first 'needle' found at: " << int(found) << endl;
      
        found=str.find("needles are small",found+1,6);
        if (found!=string::npos)
          cout << "second 'needle' found at: " << int(found) << endl;
      
        found=str.find("haystack");
        if (found!=string::npos)
          cout << "'haystack' also found at: " << int(found) << endl;
      
        found=str.find('.');
        if (found!=string::npos)
          cout << "Period found at: " << int(found) << endl;
      
        // let's replace the first needle:
        str.replace(str.find(str2),str2.length(),"preposition");
        cout << str << endl;
      
        return 0;
      }
      

      【讨论】:

      • @sehe:链接文档中有一个(我现在已经重新复制了)。显然有点太晚了;)
      猜你喜欢
      • 1970-01-01
      • 2017-11-20
      • 1970-01-01
      • 2017-10-02
      • 2012-05-21
      • 1970-01-01
      • 2016-03-06
      • 2020-08-20
      • 2015-02-18
      相关资源
      最近更新 更多