【问题标题】:In-place insert characters into string at given positions in linear time [closed]在线性时间内在给定位置将字符就地插入字符串[关闭]
【发布时间】:2018-04-13 19:08:39
【问题描述】:

我有一个std::string 和一系列ints。我想在ints 指示的位置之前添加字符。此操作应就地执行。举个例子:

string: "abcde"
positions: 1, 3
character to be inserted: ' '
=> string after modification: "a bc de"

从算法上讲,在 O(n) 时间内实现它是没有问题的,但我怀疑这已经在某个地方实现了,例如促进。但是,我在那里找不到实现。是否有提供此功能的 C++ 库?

【问题讨论】:

  • 怎么样:std::string::insert()?只需迭代位置并添加您想要的任何字符。
  • @DimChtz 在多次调用时会做不必要的字符移动,尽管它仍然是 O(n)
  • 取决于“线性时间”的含义。在std::string 中间插入字符是O(n)n 是字符串长度。所以在不同的位置插入X字符不会影响复杂度,因为它独立于n,所以它仍然是O(n)。但我怀疑这不是来自其他一些愚蠢的在线测验网站的这个明显的、愚蠢的问题的真正意图;尽管预期的答案并不复杂,但 stackoverflow.com 并不是真正让别人从一些愚蠢的在线测验网站上找出一个愚蠢的问题的地方。
  • 严格来说,使用insert 会给你 O(n*m) 其中 n 是字符串大小,m 是插入次数。 OP 要求 O(n)。
  • @SamVarshavchik 我同意。此外,我知道如何在 O(n) 时间内实现这个函数(如我的描述中所写),但我特别想知道我是否必须自己实现这个算法,或者是否已经有一个库我可以使用它。

标签: c++ string algorithm boost data-structures


【解决方案1】:

更新对于更新后的问题,其中包括就地操作的要求:

Live On Coliru

#include <algorithm>
#include <vector>
#include <cassert>
#include <array>

template <typename What>
void multi_insert(std::string& text, std::vector<size_t> positions, What const& insertion) {
    using std::begin;
    using std::end;
    assert(std::is_sorted(positions.begin(), positions.end()));

    auto source_n  = text.length();
    auto extension = insertion.size() * positions.size();

    text.resize(text.length() + extension);

    using Rit     = std::string::reverse_iterator;
    Rit dest_it   = text.rbegin(),
        source_it = dest_it + extension;

    auto posit = positions.rbegin();

    while (dest_it != source_it) {
        assert(*posit <= source_n);

        for (;source_n && *posit != source_n; --source_n)
            *dest_it++ = *source_it++;

        dest_it = std::copy(std::make_reverse_iterator(insertion.end()), std::make_reverse_iterator(insertion.begin()), dest_it);
        ++posit;
    }
}

void multi_insert(std::string& text, std::vector<size_t> positions, char insertion = ' ') {
    multi_insert(text, std::move(positions), std::array<char, 1> {{insertion}});
}

#include <iostream>

static size_t constexpr NONE = -1;

template <typename What>
void do_tests(What const& insertion) {
    std::vector<size_t> p;

    for (size_t a : { NONE, 0ul, 1ul, 2ul, 3ul, 4ul, 5ul })
    for (size_t b : { NONE, 0ul, 1ul, 2ul, 3ul, 4ul, 5ul })
    {
        if (b<a) 
            continue;

        p.assign({a,b});
        p.erase(std::remove_if(p.begin(), p.end(), [](auto n) { return n == NONE; }), p.end());

        std::string text = "abcde";
        multi_insert(text, p, insertion);

        auto opt = [](size_t n) -> char { return n==NONE?'.':('0'+n); };
        std::cout << opt(a) << " " << opt(b) << " Result: '" << text << "'\n";
    }
}

int main() {
    do_tests('!');
    do_tests(std::string("***"));
}

打印

. . Result: 'abcde'
0 . Result: '!abcde'
0 0 Result: '!!abcde'
0 1 Result: '!a!bcde'
0 2 Result: '!ab!cde'
0 3 Result: '!abc!de'
0 4 Result: '!abcd!e'
0 5 Result: '!abcde!'
1 . Result: 'a!bcde'
1 1 Result: 'a!!bcde'
1 2 Result: 'a!b!cde'
1 3 Result: 'a!bc!de'
1 4 Result: 'a!bcd!e'
1 5 Result: 'a!bcde!'
2 . Result: 'ab!cde'
2 2 Result: 'ab!!cde'
2 3 Result: 'ab!c!de'
2 4 Result: 'ab!cd!e'
2 5 Result: 'ab!cde!'
3 . Result: 'abc!de'
3 3 Result: 'abc!!de'
3 4 Result: 'abc!d!e'
3 5 Result: 'abc!de!'
4 . Result: 'abcd!e'
4 4 Result: 'abcd!!e'
4 5 Result: 'abcd!e!'
5 . Result: 'abcde!'
5 5 Result: 'abcde!!'
. . Result: 'abcde'
0 . Result: '***abcde'
0 0 Result: '******abcde'
0 1 Result: '***a***bcde'
0 2 Result: '***ab***cde'
0 3 Result: '***abc***de'
0 4 Result: '***abcd***e'
0 5 Result: '***abcde***'
1 . Result: 'a***bcde'
1 1 Result: 'a******bcde'
1 2 Result: 'a***b***cde'
1 3 Result: 'a***bc***de'
1 4 Result: 'a***bcd***e'
1 5 Result: 'a***bcde***'
2 . Result: 'ab***cde'
2 2 Result: 'ab******cde'
2 3 Result: 'ab***c***de'
2 4 Result: 'ab***cd***e'
2 5 Result: 'ab***cde***'
3 . Result: 'abc***de'
3 3 Result: 'abc******de'
3 4 Result: 'abc***d***e'
3 5 Result: 'abc***de***'
4 . Result: 'abcd***e'
4 4 Result: 'abcd******e'
4 5 Result: 'abcd***e***'
5 . Result: 'abcde***'
5 5 Result: 'abcde******'

【讨论】:

  • 添加了复制、流式版本(即采用输入和输出迭代器)coliru.stacked-crooked.com/a/0e5dde1d22670080
  • 谢谢,但就地变体不是二次方的(例如,您想在字符串中的每个字符之前插入一个字符的情况)? (不过,无需更新答案;我只是想确保我没有忽略某些东西。)
  • @phimuemue 是的。
  • @phimuemue 我看到您在问题中明确添加了“就地”要求。您可以通过倒退并提前计算预期长度来实现它。我会在大约 2 小时后添加它(在这里做饭)
  • 好的。终于搞定了:Live On Coliru 有微优化的空间,但在分析您的目标架构之前我不会打扰。这是预取真正毁掉纸面上更好的算法复杂度的情况之一
【解决方案2】:

我想不出一个完全可以做到这一点的标准实现,但您可以使用以下方法实现:

int main() {
  std::string input { "abcde" };
  std::set<size_t> positions { 1, 3 };
  char separator = ' ';

  std::string output;
  output.reserve(input.size() + positions.size()); // Make sure (at most) a single allocation is performed

  unsigned index = 0;
  auto pos = positions.begin();
  for (auto & i : input)
  {
    if (pos != positions.end() && *pos == index)
    {
      output += separator;
      ++pos;
    }

    output += i;
    ++index;
  }

  std::cout << output << std::endl;
}

假设n 是输入字符串的长度,m 是位置向量的长度,则执行:

O(n + m) [Allocation] + O(n + m) [Copy] = O(n + m) [Total].

【讨论】:

  • 谢谢,但我正在寻找就地版本。 (对不起,这是从我原来的问题中删除的。)
猜你喜欢
  • 1970-01-01
  • 2012-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-30
相关资源
最近更新 更多