高效的字符串乘法

时间:2013-08-15 18:24:23

标签: c++ string optimization

http://ideone.com/g7rGS7

您可以看到它超出了时间限制。

有人给我的想法是拥有10个左右空格的静态变量并将它们连接起来形成更大的空间,所以我想尝试通过2的幂来做到这一点。代码有效,但它显然很慢。有什么更快的方法呢?

std::string operator*(std::string const &s, size_t n)
{
    std::string r;
    r.reserve(n * s.size());
    for (size_t i=0; i<n; i++)
        r += s;
    return r;
}

std::string operator^(std::string const &s, size_t n)
{
    std::string r = s;
    for (size_t i = 1; i < n; i++)
    {
        r = s * r.size();
    }
    if (n == 0) return std::string(" ");
    return r;
}

int main()
{
    string blank = " ";
    string blank2 = blank * 2;
    string blank4 = blank2 ^ 2;
    string blank8 = blank2 ^ 3;
    string blank16 = blank2 ^ 4;

    for (int i = 0; i < 100; i++)
        assert((blank2 ^ i).size() == pow(2, i));

    return 0;
}

3 个答案:

答案 0 :(得分:1)

您的运营商^进行了大量的字符串分配。运算符*预先分配字符串,这是好的,但是每次调用operator *时,运算符^都会创建一个中间字符串。而是预先计算r的长度和所需的副本数。然后你可以预先分配r并执行连接,而不会创建一堆不需要的字符串。

答案 1 :(得分:1)

正如本comment所述,您可以这样做:

std::string str_double (std::string const & s)
{
    return s + s;
}

std::string operator*(std::string const &s, size_t n)
{
    return str_double(s * (n / 2)) + ((n % 2) ? s : std::string());
}

如果您的编译器和标准库支持右值引用和移动,我相信它非常有效。

但是,我认为任何事情都不会比直截了当的版本快得多,如下:

std::string operator*(std::string const &s, size_t n)
{
    std::string r;
    r.resize (n * s.size()); // note resizing, not reserving
    for (size_t i = 0, j = 0; i < n; ++i, j += s.size())
        memcpy (&(r[j]), &(s[0]), s.size());
    return r;                
}

答案 2 :(得分:0)

如果您希望用1个字符(示例中的空格)填充它,那么使它们更快的方法就是完全消除for循环:

int main()
{
    string blank2(2, ' ');
    string blank4(4, ' ');
    string blank8(8, ' ');
    string blank16(16, ' ');

    // etc

    return 0;
}

为了使它成为通用的(所以你可以用超过1个字符来做到这一点),你仍然想要限制你的循环:

std::string operator*(std::string const &s, size_t n)
{
    std::string r(n * s.size(), ' '); // initialize the string with the needed size
    for (size_t i=0; i<n; i++) // O(n)
        r += s;
    return r;
}

std::string operator^(std::string const &s, size_t n)
{
    size_t sn = std::pow(s.size(), n);
    std::string r(sn, ' ');
    for (size_t i = 1; i < sn; i++) // since this doesn't have an inner loop in the * operator, it is O(n) instead of O(n^2)
    {
        r += s;
    }
    return r;
}

int main()
{
    string blank = " ";
    string blank2 = blank * 2;
    string blank4 = blank2 ^ 2;
    string blank8 = blank2 ^ 3;
    string blank16 = blank2 ^ 4;

    for (int i = 0; i < 100; i++)
        assert((blank2 ^ i).size() == pow(2, i));

    return 0;
}