查找子字符串的出现次数

时间:2015-12-14 14:17:05

标签: c++ string performance find find-occurrences

我有一个小问题。我正在解决一个编程任务,但是遇到了问题。这很简单,但是时间限制会让它变得更难。

  

查找子字符串的出现次数。你将获得M - 长度   子串;要查找的子字符串,N - 基本字符串的长度;基础   字符串。
   M <= 100 000
   N <= 200 000

     

输入

     

10
      budsvabbud
      79个
  uaahskuskamikrofonu的 budsvabbud nebudlabutkspkspkspmusimriesit的 budsvabbudsvabbud NEL

     

输出
   3

我尝试使用内置函数find,但它不够快:

#include<iostream>
#include<string>

using namespace std;

int main()
{
    int n;
    int occurrences = 0;
    string::size_type start = 0;
    string base_string, to_find;
    cin >> n >> to_find >> n >> base_string;
    while ((start = base_string.find(to_find, start)) != string::npos) {
        ++occurrences;
        start++;; // see the note
    }
    cout << occurrences << endl;
}

所以我尝试编写自己的函数,但速度更慢了:

#include<iostream>
#include<cstdio>
#include<string>
#include<queue>

using namespace std;

int main()
{
    int n, m;
    string to_find;
    queue<int> rada;  
    int occurrences = 0;
    cin >> m >> to_find >> n;
    for (int i = 0; i < n; i++)
    {
        char c;
        scanf(" %c", &c);
        int max = rada.size();
        for (int j = 0; j < max; j++)
        {
            int index = rada.front();
            rada.pop();
            if (c == to_find[index])  
            {
                if (++index == m) {
                    occurrences++;
                }
                else
                    rada.push(index);
            }
        }
        if (c == to_find[0])
        {
            if (1 == m)
                n++;
            else
                rada.push(1);
        }
    }
    cout << occurrences << endl;

}

我知道有些人在0毫秒内完成了这项工作,但我的第一个代码需要超过2000毫秒而第二个代码需要的时间远远超过2000毫秒。你有什么想法如何解决这个问题? 感谢。

编辑: 长度限制:

M&lt; = 100 000 - 子串的长度

N <= 200 000 - 基本字符串的长度

3 个答案:

答案 0 :(得分:2)

您提供的算法是O(M * N),其中N是文本的长度,M是搜索世界的长度。通常,库也实现了朴素算法。然而,Knuth,Morrison和Pratt有一种算法,它在O(M + N)时间内完成。参见,例如,维基百科Knuth-Morrison-Pratt Algorithm。它有一些变体,可能更容易实现,如Boyer-Moore-Horsepool

答案 1 :(得分:1)

安全版

static size_t findOccurences(const char * const aInput, const char * const aDelim)
{
    if (aInput == 0x0 || aDelim == 0x0)
    {
        throw std::runtime_error("Argument(s) null");
    }

    const size_t inputLength = strlen(aInput);
    const size_t delimLength = strlen(aDelim);

    size_t result = 0;

    if (delimLength <= inputLength && delimLength > 0)
    {
        size_t delimIndex = 0;

        for (size_t inputIndex = 0; inputIndex < inputLength; inputIndex++)
        {
            if (aInput[inputIndex] != aDelim[delimIndex])
            {
                delimIndex = 0;
            }
            else
            {
                delimIndex++;

                if (delimIndex == delimLength)
                {
                    delimIndex = 0;
                    result++;
                }
            }
        }
    }

    return result;
}

不安全版本

static size_t unsafeFindOccurences(const char * const aInput, const char * const aDelim)
{
    const size_t inputLength = strlen(aInput);
    const size_t delimLength = strlen(aDelim);

    size_t result = 0;
    size_t delimIndex = 0;

    for (size_t inputIndex = 0; inputIndex < inputLength; inputIndex++)
    {
        if (aInput[inputIndex] != aDelim[delimIndex])
        {
            delimIndex = 0;
        }
        else
        {
            delimIndex++;

            if (delimIndex == delimLength)
            {
                delimIndex = 0;
                result++;
            }
        }
    }

    return result;
}

结果安全

          x86        x64
Debug     5501ms     5813ms
Release   3889ms     3998ms

结果不安全

          x86        x64
Debug     5442ms     5564ms
Release   3074ms     3139ms

使用Windows 10 x64 Pro下的Visual Studio 2015,Visual Studio 2015(v140)工具集编译。

使用this输入。搜索“广告”和1.000.000次迭代。

答案 2 :(得分:0)

我在调试模式下尝试此代码而没有任何优化,并且 11毫秒。 VS.NET 2013,英特尔酷睿i7:

int main()
{
    int n;
    int occurrences = 0;
    string::size_type start = 0;
    string base_string, to_find;
    base_string.reserve(200000);
    to_find.reserve(100000);
    for (size_t i = 0; i < 100000; i++){
        base_string.push_back('a');
    }
    for (size_t i = 0; i < 100000; i++){
        base_string.push_back('b');
    }
    for (size_t i = 0; i < 100000; i++){
        to_find.push_back('b');
    }
    auto start_s = clock();
    while ((start = base_string.find(to_find, start)) != string::npos) {
        ++occurrences;
        start++;; // see the note
    }
    auto stop_s = clock();
    std::cout << (stop_s - start_s) / double(CLOCKS_PER_SEC) * 1000;
    cout << occurrences << endl;
    std::getchar();
}

编译器,配置,您的计算机存在问题,但在您的代码中存在问题。