我正在尝试找到一种找到字符串模式并进行比较的最佳方法。例如,我有s1 =“红蓝蓝红黄红”,s2 =“ abbaac ”。这将匹配,因为它们具有相同的模式。
我想这样做是通过s1和s2迭代,使用向量容器来记录相应位置的计数(对于s1将是对应的单词的计数,对于s2将是对应的字母的计数)然后进行比较。
这非常低效,因为我通过整个s1和s2进行迭代。如果s1 =“红蓝红红黄红”并且s2 =“ abbaac ”。在第三个红色之后,基本上没有必要继续迭代它。
那么,关于如何做到这一点有更好的想法吗?
代码:
#include "stdafx.h"
#include <iostream>
#include <string>
#include <array>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> findPattern(string pattern){
vector<int> counts;
for (int i = 0; i < pattern.size(); ++i){
counts.push_back(0);
int counter = 0;
for (int j = i + 1; j < pattern.size(); ++j){
if (pattern[i] == pattern[j]){
++counter;
}
counts[i] = counter;
}
}
return counts;
}
vector<int> findPatternLong(string pattern){
istringstream iss (pattern);
string word;
vector<string> v;
while (iss >> word){
v.push_back(word);
}
vector<int> counts2;
for (int i = 0; i < v.size(); ++i){
counts2.push_back(0);
int counter = 0;
for (int j = i + 1; j < v.size(); ++j){
if (v[i] == v[j]){
++counter;
}
counts2[i] = counter;
}
}
return counts2;
}
int main(int argc, char * argv[]){
vector<int> v1 = findPattern("abbaac");
vector<int> v2 = findPatternLong("red blue blue red red yellow");
if (v1.size() == v2.size()){
for (int i = 0; i < v1.size(); ++i){
if (v1[i] != v2[i]){
cout << "Unmatch" << endl;
return false;
}
}
cout << "match" << endl;
return true;
} else
cout << "Unmatch" << endl;
return 0;
}
答案 0 :(得分:2)
首先,不要过分担心效率,注重正确性:事实上,过早优化是万恶之源。编写测试用例并确保代码通过每个测试用例。
其次,我想我会从地图/字典D开始,并且有一个循环,我将解析每个字符串中的一个元素(s1中的一个单词,让我们称之为“w”)和s2中的一个字符,说“c”),选择一个元素作为键(比如说“c”字符)并检查“c”是否已在字典中有一个条目:
如果该代码有效,则可以开始优化。在您的示例中,也许我们可以使用简单的数组而不是字典,因为ASCII字符是一个小的有限集。
答案 1 :(得分:1)
这不是最有效的代码,但接近最简单:
std::map<char, std::string> letter_to_word;
std::set<std::string> words_seen;
std::istringstream iss(s1);
std::string word;
for (std::string::size_t i = 0; i < s2.size(); ++i)
{
if (!(iss >> word))
return false; // more letters than words
std::string& expected_word = letter_to_word[s2[i]];
if (expected_word == "")
{
// if different letters require different words...
if (words_seen.find(word) != words_seen.end())
return false; // multiple letters for same word
words_seen.insert(word);
expected_word = word; // first time we've seen letter, remember associated word
}
else if (expected_word != word)
return false; // different word for same letter
}
return !(iss >> word); // check no surplus words
答案 2 :(得分:0)
您不需要两个向量。
处理第二个字符串时,将第一个模式的计数与第一个条目进行比较。如果匹配,继续停止。对第二个字符串中的其余模式重复此操作。
您无需存储第二个字符串的模式计数。
答案 3 :(得分:0)
基本上,您要检查序列是否遵循相同的顺序。你并不担心序列究竟是什么:第一个第一个第一个第三个就足够了。现在,您可以使用以某种方式将字符串映射到int的容器来执行此操作。但是,您将存储每个字符串的副本,而忽略了您并不真正关心字符串值的事实。对于微小的测试用例,这并不重要,但对于大量长字,你很快就会在不需要的时候咀嚼内存。
因此,让我们使用我们不关心字符串值或存储它们的事实。如果是这种情况,我们可以使用哈希函数将我们的字符串转换为简单的size_t值,并相当有力地保证它们将是唯一的。但是,散列不是顺序的,我们需要根据散列值检索序列。记录序列的最简单方法是将它们映射到地图的大小以便于查找。拼图的最后一部分是检查哈希是否在相同的序列中。
我还假设你不只是想将一个句子与一个单词进行比较,而是可能是两个单词或两个句子。这是一个快速的C ++ 11示例,它基本上完成了上述操作,除非需要,否则不会在内存中保留任何内容。
当然,这仍然可以进行更多优化 - 例如,执行并行操作。
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <sstream>
/*
s1 = "red blue blue red red yellow"
s2 = "abbaac"
This would match because they have the same pattern.
*/
typedef std::map<size_t,size_t> hash_map;
typedef std::vector<std::string> wordlist;
size_t ordered_symbol( hash_map &h, std::string const& word )
{
std::hash<std::string> hash_fn;
size_t hash = hash_fn(word);
if(h.find(hash)==h.end())
{
size_t const sequence = h.size();
h[hash] = sequence;
return sequence;
}
return h[hash];
}
wordlist create_wordlist( std::string const& str )
{
if(str.find_first_of(' ') != std::string::npos)
{
wordlist w1;
std::stringstream sstr(str);
std::string s;
while(sstr>>s)
w1.push_back(s);
return w1;
}
wordlist w2;
for(auto i : str)
{
std::string s;
s.append(1,i);
w2.push_back(s);
}
return w2;
}
bool pattern_matches( std::string const& s1, std::string const& s2 )
{
wordlist const w1 = create_wordlist(s1);
wordlist const w2 = create_wordlist(s2);
if(w1.size()!=w2.size())
return false;
hash_map h1,h2;
for( size_t i = 0; i!=w1.size(); ++i)
if(ordered_symbol(h1,w1[i])!=ordered_symbol(h2,w2[i]))
return false;
return true;
}
void test( std::string const& s1, std::string const& s2 )
{
std::cout<<"["<<s1<<"] "
<<(pattern_matches(s1,s2)? "<==>" : "<=!=>")
<<"["<<s2<<"]\n";
}
int main()
{
test("red blue blue red red yellow","abbaac");
test("red blue blue red red yellow","first second second first first third");
test("abbaac","12211g");
test("abbaac","red blue blue red red yellow");
test("abbgac","red blue blue red red yellow");
return 0;
}
//Output:
//[red blue blue red red yellow] <==>[abbaac]
//[red blue blue red red yellow] <==>[first second second first first third]
//[abbaac] <==>[12211g]
//[abbaac] <==>[red blue blue red red yellow]
//[abbgac] <=!=>[red blue blue red red yellow]
编辑:这是non C++11 version,应该适用于VS2010。但是,由于C ++ 03在标准库中不包含字符串散列函数,因此本示例使用从堆栈溢出中获取的散列函数。如果您可以访问boost库,那么使用的更好的哈希函数将是this one。
答案 4 :(得分:0)
编辑
我刚刚读到这个问题在字符串中有模式,这个答案适用于比较不同类型的集合。我想,如果首先转换了2个输入字符串,答案仍然会包含 little 水:)
我不会说这是最有效的解决方案,但我喜欢它是如何可扩展的。
首先,有PatternResult
类。它存储模式的结果:
class PatternResult {
private:
std::vector<int> result_;
public:
PatternResult(const std::vector<int>& result) : result_(result) {
};
bool operator == (const PatternResult& rhs) const {
if(result_.size() != rhs.result_.size())
return false;
else {
for(std::vector<int>::size_type r(0);
r < result_.size();
++r) {
if(result_[r] != rhs.result_[r])
return false;
};
return true;
};
};
}; // eo class PatternResult
它采用整数向量,其值表示它的值。我们重载==
来比较两个模式结果,这意味着它们具有相同的序列无论源数据。
然后我们需要一个可以分配相同序列号的模式计数器,但需要任何类型:
template<class T>
class PatternCounter {
private:
typedef std::vector<T> vec_type;
typedef std::map<T, int> map_type;
map_type found_;
int counter_;
public:
PatternCounter() : counter_(1) {
};
PatternResult count(const vec_type& input ){
std::vector<int> ret;
for(vec_type::const_iterator cit(input.begin());
cit != input.end();
++cit) {
if(found_.find(*cit) != found_.end()) {
ret.push_back(found_[*cit]);
} else {
found_[*cit] = counter_;
ret.push_back(counter_);
++counter_;
};
};
return PatternResult(ret);
};
};
我们已经完成了。测试代码:
std::vector<std::string> inp1;
inp1.push_back("red");
inp1.push_back("blue");
inp1.push_back("blue");
inp1.push_back("red");
inp1.push_back("yellow");
std::vector<char> inp2;
inp2.push_back('a');
inp2.push_back('b');
inp2.push_back('b');
inp2.push_back('a');
inp2.push_back('c');
PatternCounter<std::string> counter1;
PatternCounter<char> counter2;
PatternResult res1(counter1.count(inp1));
PatternResult res2(counter2.count(inp2));
if(res1 == res2) {
// pattern sequences are equal
};
请注意,这很快,很脏,我相信它可以提高效率。