Помогите реализовать алгоритм
От:
momart
Дата: 22.09.04 13:52
Оценка:
Здравствуйте!
Есть такая задача:
строка [word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2] и так далее. Надо оставить только первые вхождения слов в скобках:
[word1] bla-bla-bla [word2] tra-ta-ta be-be-be [word3] a-a-a
Как можно это красивей всего сделать, может использовать рег. выражения? (ограничений на исп. .NET нет), или и без рег. выражений можно быстро сделать?
Спасибо!
23.09.04 14:59: Перенесено из 'C/C++'
Re: Помогите реализовать алгоритм
От:
korzhik
Дата: 22.09.04 20:38
Оценка:
Здравствуйте, momart, Вы писали:
M>Здравствуйте!
M>Есть такая задача:
M>строка [word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2] и так далее. Надо оставить только первые вхождения слов в скобках:
M>[word1] bla-bla-bla [word2] tra-ta-ta be-be-be [word3] a-a-a
вот мой вариант:
#include <iostream>
#include <string>
#include <vector>
template <typename Iterator>
class tokenizer
{
private :
tokenizer(const tokenizer&);
tokenizer& operator =(const tokenizer&);
public :
struct token
{
Iterator first;
Iterator last;
};
public :
tokenizer()
{
}
void set_str(Iterator first, Iterator last)
{
first_ = first;
last_ = last;
current_= first_;
}
token current_token()
{
return token_;
}
bool next();
private :
Iterator first_;
Iterator last_;
Iterator current_;
token token_;
};
//-----------------------------------------------------------------------
template <typename Iterator>
bool tokenizer<Iterator>::next()
{
if (current_ == last_)
{
return false ;
}
while (current_ != last_ && *current_ != '[' )
{
++current_;
}
if (current_ == last_)
{
return false ;
}
unsigned count = 0;
Iterator p = current_;
while (p != last_ && *p != ']' )
{
++p;
++count;
}
++count;
if (p == last_)
{
return false ;
}
token_.first = current_;
token_.last = current_ + count;
current_ += count;
return true ;
}
//-----------------------------------------------------------------------
int main()
{
std::string str("[word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2]" );
typedef tokenizer<std::string::iterator> tokenizer;
tokenizer tok;
tok.set_str(str.begin(), str.end());
std::vector<tokenizer::token> tokens;
while (tok.next())
{
tokenizer::token tk = tok.current_token();
std::vector<tokenizer::token>::iterator it = tokens.begin();
std::vector<tokenizer::token>::iterator last = tokens.end();
for (;it != last; ++it)
{
if (std::distance(it->last, it->first) == std::distance(tk.last, tk.first) &&
std::string(it->first, it->last) == std::string(tk.first, tk.last))
{
tok.set_str(str.erase(tk.first, tk.last), str.end());
}
}
tokens.push_back(tok.current_token());
}
std::cout << str << std::endl;
}
кто будет критиковать код сразу сообщаю, что я писал его уставший и ,честно говоря, немного выпивший, так что особо не ругайте.
Re[2]: Помогите реализовать алгоритм
От:
korzhik
Дата: 23.09.04 09:20
Оценка:
Здравствуйте, korzhik, Вы писали:
маленькие исправления:
while (tok.next())
{
bool bad_token = false ;
tokenizer::token tk = tok.current_token();
std::vector<tokenizer::token>::iterator it = tokens.begin();
std::vector<tokenizer::token>::iterator last = tokens.end();
for (;it != last; ++it)
{
if (std::distance(it->last, it->first) == std::distance(tk.last, tk.first) &&
std::string(it->first, it->last) == std::string(tk.first, tk.last))
{
tok.set_str(str.erase(tk.first, tk.last), str.end());
bad_token = true ;
break ;
}
}
if (!bad_token)
tokens.push_back(tok.current_token());
}
Re[3]: Помогите реализовать алгоритм
От:
momart
Дата: 23.09.04 10:14
Оценка:
Здравствуйте, korzhik.
Спасибо за помощь
. Ваша реализация чудесно работает. Хотя, мне все же интересно (из чистого любопытства) как можно это сделать с помощью регулярных выражений (мне кажется это будет пара строчек кода).
Re[4]: Помогите реализовать алгоритм
От:
Кодт
Дата: 23.09.04 10:48
Оценка:
Здравствуйте, momart, Вы писали:
M>Спасибо за помощь . Ваша реализация чудесно работает. Хотя, мне все же интересно (из чистого любопытства) как можно это сделать с помощью регулярных выражений (мне кажется это будет пара строчек кода).
Ну не пара...
1) эскейпить префикс маркера: /&/&/g
2) Получить с помощью регекспа массив всех слов: /\[(.*?)\]/g
3) для
каждого слова $word из массива выполнить замену:
3.1) заменить первое вхождение на маркер /\[$word\]/▮/
3.2) заменить все (оставшиеся) на пусто /\[$word\]//g
3.3) заменить маркер на значение /\▮/[$word]/
4) разэскейпить префикс: /\&/&/g
На перле это будет ровно столько строк, сколько здесь.
Перекуём баги на фичи!
Re[5]: Помогите реализовать алгоритм
К>На перле это будет ровно столько строк, сколько здесь.
my $src = "a&a [A] b [B] c&c [C] d [A] e [B] f [C] g [A] h" ;
print "string: $src\n" ;
## 1
$src =~ s /\&/&/g;
print "reserve marker: $src\n" ;
## 2
my @words = ($src =~ /\[(.*?)\]/g);
## 3
foreach my $word (@words) {
print "word is $word\n" ;
## 3.1
$src =~ s /\[$word\]/&mark;/;
print "...replace first: $src\n" ;
## 3.2
$src =~ s /\[$word\]//g;
print "...remove others: $src\n" ;
## 3.3
$src =~ s /\&mark;/[$word]/;
print "...restore first: $src\n" ;
}
## 4
$src =~ s /\&/&/g;
print "restore marker: $src\n" ;
Перекуём баги на фичи!
Re[2]: Помогите реализовать алгоритм
Здравствуйте, korzhik, Вы писали:
K>Здравствуйте, momart, Вы писали:
M>>Здравствуйте!
M>>Есть такая задача:
M>>строка [word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2] и так далее. Надо оставить только первые вхождения слов в скобках:
M>>[word1] bla-bla-bla [word2] tra-ta-ta be-be-be [word3] a-a-a
K>вот мой вариант:
K>K>#include <iostream>
K>#include <string>
K>#include <vector>
K>template <typename Iterator>
K>class tokenizer
K>{
K>private :
K> tokenizer(const tokenizer&);
K> tokenizer& operator =(const tokenizer&);
K>public :
K> struct token
K> {
K> Iterator first;
K> Iterator last;
K> };
K>public :
K> tokenizer()
K> {
K> }
K> void set_str(Iterator first, Iterator last)
K> {
K> first_ = first;
K> last_ = last;
K> current_= first_;
K> }
K> token current_token()
K> {
K> return token_;
K> }
K> bool next();
K>private :
K> Iterator first_;
K> Iterator last_;
K> Iterator current_;
K> token token_;
K>};
K>//-----------------------------------------------------------------------
K>template <typename Iterator>
K>bool tokenizer<Iterator>::next()
K>{
K> if (current_ == last_)
K> {
K> return false ;
K> }
K> while (current_ != last_ && *current_ != '[' )
K> {
K> ++current_;
K> }
K> if (current_ == last_)
K> {
K> return false ;
K> }
K> unsigned count = 0;
K> Iterator p = current_;
K> while (p != last_ && *p != ']' )
K> {
K> ++p;
K> ++count;
K> }
K> ++count;
K> if (p == last_)
K> {
K> return false ;
K> }
K> token_.first = current_;
K> token_.last = current_ + count;
K> current_ += count;
K> return true ;
K>}
K>//-----------------------------------------------------------------------
K>int main()
K>{
K> std::string str("[word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2]" );
K> typedef tokenizer<std::string::iterator> tokenizer;
K> tokenizer tok;
K> tok.set_str(str.begin(), str.end());
K> std::vector<tokenizer::token> tokens;
K> while (tok.next())
K> {
K> tokenizer::token tk = tok.current_token();
K> std::vector<tokenizer::token>::iterator it = tokens.begin();
K> std::vector<tokenizer::token>::iterator last = tokens.end();
K> for (;it != last; ++it)
K> {
K> if (std::distance(it->last, it->first) == std::distance(tk.last, tk.first) &&
K> std::string(it->first, it->last) == std::string(tk.first, tk.last))
K> {
K> tok.set_str(str.erase(tk.first, tk.last), str.end());
K> }
K> }
K> tokens.push_back(tok.current_token());
K> }
K> std::cout << str << std::endl;
K>}
K>
K>кто будет критиковать код сразу сообщаю, что я писал его уставший и ,честно говоря, немного выпивший, так что особо не ругайте.
И все-таки покритикуем. Правда не код, а алгоритм. Здесь все хорошо за исключением квадратичности. Лучше вместо внутреннего цикла использовать HashSet для проверки того, что такой тоукен уже встречался. Алгоритм будет работать в N раз быстрее (где N-количество слов в []).
Можно подумать и о том, чтобы заменить удаление во внутреннем цикле на добавление к результирующей строке. В крайнем случае сейчас мы произведем N-1 удаление, при этом всего произойдет N-1 копирование ВСЕЙ оставшейса строки.
Re[3]: Помогите реализовать алгоритм
От:
korzhik
Дата: 23.09.04 13:33
Оценка:
Здравствуйте, investigator, Вы писали:
I>И все-таки покритикуем.
люблю конструктивную критику
>Лучше вместо внутреннего цикла использовать HashSet для проверки того, что такой тоукен уже встречался.
согласен
Re[3]: Помогите реализовать алгоритм
От:
korzhik
Дата: 23.09.04 15:35
Оценка:
Здравствуйте, investigator, Вы писали:
I>Можно подумать и о том, чтобы заменить удаление во внутреннем цикле на добавление к результирующей строке.
это хорошее предложение
std::string kill_dup(const std::string& source)
{
typedef tokenizer<std::string::const_iterator> tokenizer;
std::string::const_iterator start = source.begin();
std::vector<tokenizer::token> tokens;
std::string result;
tokenizer tok(source.begin(), source.end());
while (tok.next())
{
typedef std::vector<tokenizer::token>::iterator iterator;
iterator it = tokens.begin();
iterator last = tokens.end();
tokenizer::token tk = tok.current_token();
while ( it != last &&
!(std::distance(it->last, it->first) == std::distance(tk.last, tk.first) &&
std::string(it->first, it->last) == std::string(tk.first, tk.last)))
{
++it;
}
if (it != last)
{
result.append(start, tk.first);
start = ++tk.last;
}
else
{
tokens.push_back(tk);
}
}
return result;
}
//-----------------------------------------------------------------------
int main()
{
std::cout
<< kill_dup("[word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2]" )
<< std::endl;
}
Re[2]: Помогите реализовать алгоритм
От:
korzhik
Дата: 23.09.04 18:25
Оценка:
Здравствуйте, korzhik, Вы писали:
K>вот мой вариант:
ну в общем вот последний вариант.
не много порефакторил, исправил баги.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
//-----------------------------------------------------------------------
template <typename Iterator>
class tokenizer
{
private :
tokenizer(const tokenizer&);
tokenizer& operator =(const tokenizer&);
public :
struct token { Iterator first, last; };
tokenizer(Iterator first, Iterator last) : current_(first), last_(last){}
token current_token() const { return token_;}
bool next()
{
return last_ == (current_ = std::find(current_, last_, '[' )) ?
false :
last_ != (current_ = token_.last = std::find(token_.first = current_, last_, ']' ));
}
private :
const Iterator last_;
Iterator current_;
token token_;
};
namespace
{
class tokencmp
{
typedef tokenizer<std::string::const_iterator> tokenizer;
tokenizer::token tk_;
public :
tokencmp(const tokenizer::token& tk) : tk_(tk) {}
bool operator ()(const tokenizer::token& tk) const
{
return std::string(tk.first, tk.last) == std::string(tk_.first, tk_.last);
}
};
} // unnamed namespace
//-----------------------------------------------------------------------
std::string kill_dup(const std::string& source)
{
typedef tokenizer<std::string::const_iterator> tokenizer;
std::string::const_iterator start = source.begin();
std::vector<tokenizer::token> tokens;
std::string result;
tokenizer tok(source.begin(), source.end());
while (tok.next())
{
tokenizer::token tk = tok.current_token();
if (tokens.end() != std::find_if(tokens.begin(), tokens.end(), tokencmp(tk)))
{
result.append(start, tk.first);
start = ++tk.last;
}
else
{
tokens.push_back(tk);
}
}
return result;
}
//-----------------------------------------------------------------------
int main()
{
std::cout
<< kill_dup("[word1] bla-bla-bla [word2] tra-ta-ta [word1] be-be-be [word3] a-a-a [word2]" )
<< std::endl;
}
Пока на собственное сообщение не было ответов, его можно удалить.
Удалить