超快速搜索整数子字符串的方法
Ultra fast way to search for an integer substring
我的处境是,我需要在一系列非常大的字符串中找到一个整数子字符串。我想到了使用向量中的向量来存储整数字符串的范围,类似地,我将要搜索的整数字符串存储在向量中。以下示例:
//vector of 5 vectors
std::vector<std::vector<int>> vec(5);
// elements= {10,5,8,23,15,32,12,34,56,55,43,12,33,4}
并将子串转换为vector
//vector with integer substring
std::vector<int> vec1;
//elements = {5,8,23}
我使用std::search
在vector of vectors
上执行搜索操作,以找到vector
,类似于这个
for( int i = 0; i < vec.size(); i++) // searching read into
{
auto pos = std::search(vec[i].begin(), vec[i].end(), vec1.begin(), vec1.end());
// some more code
}
在测试中,从每个长度为500000
的10 vectors
的范围中搜索1000 strings
大约需要1m
。
有一些数据结构是超快速的,比如unordered_map
,但我怀疑是否要将数据结构用于我的数据。如果有任何关于container
或data structure
的建议或链接在时间和空间上都是有效的,我将不胜感激。
注意:
1) 不可能对数据进行排序,因为我通过排序来松散数据表示。
2) 我不是在搜索单个项目,实际上是在搜索整数的子字符串。
编辑
字符串的原始长度可以是每个向量中的100000000
,子串的长度可以是数量上的100
、1million
。
这是我尝试的快速解决方案——在我的2.7GHz Mac mini上,它能够在1357毫秒内找到1000个"子字符串"的位置。它首先为每个整数出现在大向量中的所有位置建立索引,这样就不必到处搜索每个子字符串,而是只搜索该子字符串可能实际开始的位置。需要注意的一点是,索引占用了相当多的额外RAM,并且需要一些时间来构建;因此,根据您的用例,这可能是一个实用的解决方案,也可能不是。(但请注意,它只需要构建一次,除非/直到你继续搜索一组不同的大向量)
#include <algorithm>
#include <vector>
#include <cmath>
#include <cstdint>
#include <chrono>
#include <iostream>
#include <unordered_map>
using namespace std;
// Store a vector index and an offset into the vector efficiently
// Supports up to 256 vectors and offsets up to 16777216
static inline uint32_t GetVectorLocationKey(uint8_t whichVector, uint32_t offsetIntoVector)
{
return ((((uint32_t)whichVector)<<24)|offsetIntoVector);
}
static inline void GetVectorLocationFromKey(uint32_t key, uint8_t & retWhichVector, uint32_t & retOffsetIntoVector)
{
retWhichVector = (key >> 24) & 0xFF;
retOffsetIntoVector = (key & 0xFFFFFF);
}
static inline bool SubstringExistsAtOffset(const int * bigVector, const vector<int> & substring)
{
const int * smallVector = &substring[0];
const size_t subLen = substring.size();
for (size_t i=0; i<subLen; i++) if (bigVector[i] != smallVector[i]) return false;
return true;
}
int main(int, char **)
{
// Create some large vectors to search in
vector<vector<int> > big_vectors;
const size_t num_big_vectors = 5;
const size_t big_vector_size = 500000;
for (size_t i=0; i<num_big_vectors; i++)
{
big_vectors.push_back(vector<int>());
vector<int> & v = big_vectors.back();
for (size_t j=0; j<big_vector_size; j++) v.push_back(rand()%100);
}
// Pick out some small "substring" vectors to search for within the large vectors
vector<vector<int> > substrings;
const size_t num_substrings = 1000;
const size_t substring_size = 14;
for (size_t i=0; i<num_substrings; i++)
{
substrings.push_back(vector<int>());
size_t whichBigVector = rand()%num_big_vectors;
size_t offsetIntoVector = rand()%(big_vector_size-substring_size);
vector<int> & v = substrings.back();
const vector<int> & bigVector = big_vectors[whichBigVector];
for (size_t j=0; j<substring_size; j++) v.push_back(bigVector[offsetIntoVector+j]);
}
// Now we'll build up a map so that for any given integer we'll
// have immediate access to a list of the locations it is at.
// That way we can jump immediately to those locations rather than
// having to scan through the entire set of big_vectors
unordered_map<int, vector<uint32_t> > index;
for (size_t i=0; i<big_vectors.size(); i++)
{
const vector<int> & bigVector = big_vectors[i];
for (size_t j=0; j<bigVector.size()-substring_size; j++)
{
int val = bigVector[j];
index[val].push_back(GetVectorLocationKey(i, j));
}
}
// Now for the time-critical part: Let's see how fast we
// can find our substrings within the larger vectors!
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
vector<vector<uint32_t> > results;
for (size_t i=0; i<substrings.size(); i++)
{
results.push_back(vector<uint32_t>());
vector<uint32_t> & resultVec = results.back();
const vector<int> & substring = substrings[i];
const int firstVal = substring[0];
const vector<uint32_t> & lookup = index[firstVal];
for (size_t j=0; j<lookup.size(); j++)
{
const uint32_t key = lookup[j];
uint8_t whichVector;
uint32_t offsetIntoVector;
GetVectorLocationFromKey(key, whichVector, offsetIntoVector);
const vector<int> & bigVector = big_vectors[whichVector];
if (SubstringExistsAtOffset(&bigVector[offsetIntoVector], substring)) resultVec.push_back(key);
}
}
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
cout << " Total time spent finding " << substrings.size() << " substrings was " << std::chrono::duration_cast<std::chrono::milliseconds>(end-begin).count() << " milliseconds." << std::endl;
cout << endl << endl << "RESULTS:" << endl;
for(size_t i=0; i<results.size(); i++)
{
const vector<uint32_t> & result = results[i];
for (size_t j=0; j<result.size(); j++)
{
const uint32_t key = result[j];
uint8_t whichVector;
uint32_t offsetIntoVector;
GetVectorLocationFromKey(key, whichVector, offsetIntoVector);
cout << "An instance of substring #" << i << " was found in bigVector #" << (int)whichVector << " at offset " << offsetIntoVector << endl;
// Let's just double-check that the substring actually exists where I said it did
// It would be embarrassing to find out I'm not actually finding them correctly :P
const vector<int> & bigVector = big_vectors[whichVector];
const vector<int> & substring = substrings[i];
for (size_t k=0; k<substring.size(); k++)
{
if (bigVector[offsetIntoVector+k] != substring[k]) cout << "ERROR BAD RESULT in substring #" << i << " at offset " << k << endl;
}
}
}
}
相关文章:
- 有哪些有效的方法可以消除一组 100 万个字符串>重复数据?
- 在 c++ 中对类中的 c 字符串动态数组进行排序的最佳方法是什么?
- 具有字符串化的可变参数宏的现代/通用方法
- 接收字符串并使用它来调用方法C++
- 如何分隔字符串并将标记传递给方法
- 是否有通用方法可以找到任何以 null 结尾的字符串的长度?
- 当映射包含字符串向量作为值时,从值中获取键的有效方法
- 在 C++11 中字符串化变量名称的替代方法
- 连接和压缩标准::vector<std::字符串的最佳方法>
- Esp8266 & Nodemcu:返回请求字符串的方法
- 将位字符串转储到二进制文件的最佳方法是什么
- 有没有一种 STL 方法可以找到字符串的所有排列,给出一个以 C++ 为单位的大小?
- 如何将字符串从 C++/CLI 方法返回到调用它的非托管C++
- 返回混合 \ 和 / 的文件系统路径字符串方法
- VS 链接器失败,标准::字符串方法出现"object already exists"错误
- Lua:为自定义用户数据提供一个字符串方法
- 语法错误字符串方法C++
- 返回原始子字符串的子字符串方法
- 为什么我不能在C++中使用这种方法与"char"而不是"字符串"方法?
- 如何使c++字符串函数的行为像Java和c#字符串方法