搜索字符串以查找字符串列表中任何单词的出现

时间:2016-01-10 01:54:05

标签: c++ arrays string text-search

我想知道在C ++中如何在字符串中搜索任何字符串列表的第一个实例。 std::string::find_first_of()的一种全字版本:" 在字符串中搜索与其参数"中指定的任何字符匹配的第一个字符。

我想要的东西会在字符串中搜索与提供的列表/数组中的任何单词匹配的第一个WORD。要清楚,我不想在数组中搜索字符串的实例。我想搜索一个字符串,以获取数组中某个东西的实例。

我的目标是能够取一个句子,并删除列表中的所有单词。例如,如果我给它列出{"the" "brown", "over"}; 和句子,"the quick brown fox jumped over the lazy dog", 我希望它输出" quick fox jumped lazy dog"。 如果我愿意的话,我希望能够列出100个单词;我需要这个可以扩展。

我能想到的唯一解决方案是在我的文本块的std::find(stringArray[0])循环中使用while,并保存找到该单词的索引,然后将所有内容放在另一个{ {1}}循环并对我的数组中的每个单词执行此操作,将每个单词的索引保存到一个巨大的列表中。 (可选)然后对该列表进行数字排序,最后通过并删除该列表中某个位置的每个单词。

我真的希望有一个功能或更简单的方法来实现它,因为我的解决方案似乎很难且非常慢,特别是因为我需要多次使用它,在很多不同的字符串上,去通过50,000个字符的文本块的所有句子。任何更好的优化都是首选。

2 个答案:

答案 0 :(得分:1)

如果您寻找标准功能,如果您敢于将句子存储为字符串容器,则存在可能性:

string input="Hello, world ! I whish you all \na happy new year 2016 !";
vector<string> sentence; 

stringstream sst(input);    // split the string into its pieces 
string tmp; 
while (sst>>tmp) 
    sentence.push_back(tmp); 

当然,在现实世界中,你不仅可以在空格上进行分割,还可以在标点符号上进行分割。这只是一个概念证明。

一旦您使用此表单,就可以轻松使用 <algorithm> find_first_of() 形式:

vector<string> search{"We", "You", "I"}; 
auto it =  find_first_of(sentence.begin(), sentence.end(), 
                           search.begin(), search.end()); 

                           // display remaining of the sentence
copy(it , sentence.end(), ostream_iterator<string>(cout,"/"));    
cout<<endl;     

从矢量中删除单词不应该再成为问题。我把它作为练习让你。

获得清理后的矢量后,您可以重建字符串:

stringstream so;
copy(it , sentence.end(), ostream_iterator<string>(so," ")); 
string result = so.str(); 

这是 online demo

但是,此解决方案无法解决您的所有性能问题。为此,您需要进一步分析性能瓶颈的来源:您是否制作了大量不必要的对象副本?您自己的算法是否会触发大量低效的内存分配?或者它真的是纯粹的文本量?

进一步工作的一些想法:

  • 为句子中的单词构建一个按字母顺序排列的索引(map&gt; where unsigned
  • 考虑一个 trie 数据结构(trie而不是树!!)
  • <regex>
  • 中使用正则表达式

答案 1 :(得分:1)

有些人的速度很快,其他人的速度很慢,所以很难说你的意思是哪个快,50000个字符听起来不那么大,一个人必须做一些特别的事情。

唯一应该避免的是就地操作输入字符串(会导致O(n ^ 2)运行时间) - 只返回一个新的结果字符串。为结果字符串保留足够的内存可能是明智的,因为它会为某些输入保存一个常数因子。

有我的建议:

std::string remove_words(const std::string &sentence, const std::set<std::string> &words2remove, const std::string &delimiters){

    std::string result;
    result.reserve(sentence.size());//ensure there is enough place 

    std::string lastDelimiter;//no delimiter so far...
    size_t cur_position=0;
    while(true){
      size_t next=sentence.find_first_of(delimiters, cur_position);
      std::string token=sentence.substr(cur_position, next-cur_position);

      result+=lastDelimiter;
      if(words2remove.find(token)==words2remove.end())
         result+=token;//not forbidden

      if(next==std::string::npos)
        break;

      //prepare for the next iteration:  
      lastDelimiter=sentence[next];
      cur_position=next+1;
    }

    return result;
}

由于查找速度较快,此方法使用一组而不是禁用词列表。作为分隔符,可以使用任何一组字符,例如" "" ,.;"

它在O(n * log(k))中运行,其中n是句子中的字符数,k是禁止集中的单词数。

如果您需要更灵活的tokonizer并且不想重新发明轮子,您可能需要查看boost::tokonizer

如果禁用字的数量很大,您可以考虑使用std :: unordered_set(c ++ 11)或boost::unordered_set而不是std :: set来减少算法的预期运行时间为O(n)。