Я делаю ЭТУ проблему UVa, которая принимает следующий вход:
This is fun-
ny! Mr.P and I've never seen
this ice-cream flavour
before.Crazy eh?
#
This is fun-
ny! Mr.P and I've never seen
this ice-cream flavour
before.Crazy eh?
#
и производит этот выход:
1 1
2 3
3 2
4 3
5 3
6 1
7 1
8 1
1 1
2 3
3 2
4 3
5 3
6 1
7 1
8 1
На входе # делит случаи. Я должен получить длину каждого слова и подсчитать частоту каждой разной длины (как вы видите на выходе, слово длиной 1 встречается один раз, длина 2 встречается три раза, 3 - два раза и т.д.).
Моя проблема такова: при чтении в cin, before.Crazy
считается одним словом, так как нет пространства, разделяющего их. Затем должно быть так же просто, как разбиение строки на определенные знаки пунктуации (например, {".",",","!","?"}
), Но C++, похоже, не имеет простого способа разделить строка.
Итак, мой вопрос: как я могу разделить строку и отправить в каждую возвращаемую строку моей функции, которая обрабатывает остальную часть проблемы?
Вот мой код:
int main()
{
string input="";
while(cin.peek()!=-1)
{
while(cin >> input && input!="#")
{
lengthFrequency(input);
cout << input << " " << input.length() << endl;
}
if(cin.peek()!=-1) cout << endl;
lengthFrequencies.clear();
}
return 0;
}
lengthFrequency
- это map<int,int>
.
Вы можете переопределить то, что поток считает символом пробела, используя std::locale
с настраиваемой гранью std::ctype<char>
. Вот соответствующий код, который не совсем выполняет задание, но демонстрирует, как использовать грань:
#include <algorithm>
#include <iostream>
#include <locale>
#include <string>
struct ctype
: std::ctype<char>
{
typedef std::ctype<char> base;
static base::mask const* make_table(char const* spaces,
base::mask* table)
{
base::mask const* classic(base::classic_table());
std::copy(classic, classic + base::table_size, table);
for (; *spaces; ++spaces) {
table[int(*spaces)] |= base::space;
}
return table;
}
ctype(char const* spaces)
: base(make_table(spaces, table))
{
}
base::mask table[base::table_size];
};
int main()
{
std::cin.imbue(std::locale(std::locale(), new ctype(".,!?")));
for (std::string s; std::cin >> s; ) {
std::cout << "s='" << s << "'\n";
}
}
#
?
Как насчет этого (используя STL, компараторы и функторы)?
ПРИМЕЧАНИЕ. Все предположения и объяснения содержатся в самом исходном коде.
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <utility>
#include <string.h>
bool compare (const std::pair<int, int>& l, const std::pair<int, int>& r) {
return l.first < r.first;
}
//functor/unary predicate:
struct CompareFirst {
CompareFirst(int val) : val_(val) {}
bool operator()(const std::pair<int, int>& p) const {
return (val_ == p.first);
}
private:
int val_;
};
int main() {
char delims[] = ".,!?";
char noise[] ="-'";
//I'm assuming you've read the text from some file, and that information has been stored in a string. Or, the information is a string (like below):
std::string input = "This is fun-\nny, Mr.P and I've never seen\nthis ice-cream flavour\nbefore.Crazy eh?\n#\nThis is fun-\nny! Mr.P and I've never seen\nthis ice-cream flavour\nbefore.Crazy eh?\n#\n";
std::istringstream iss(input);
std::string temp;
//first split the string by #
while(std::getline(iss, temp, '#')) {
//find all the occurences of the hypens as it crosses lines, and remove the newline:
std::string::size_type begin = 0;
while(std::string::npos != (begin = temp.find('-', begin))) {
//look at the character in front of the current hypen and erase it if it a newline, if it is - remove it
if (temp[begin+1] == '\n') {
temp.erase(begin+1, 1);
}
++begin;
}
//now, erase all the 'noise' characters ("'-") as these count as these punctuation count as zero
for (int i = 0; i < strlen(noise); ++i) {
//this replaces all the hyphens and apostrophes with nothing
temp.erase(std::remove(temp.begin(), temp.end(), noise[i]), temp.end());//since hyphens occur across two lines, you need to erase newlines
}//at this point, everything is dandy for complete substitution
//now try to remove any other delim chracters by replacing them with spaces
for (int i = 0; i < strlen(delims); ++i) {
std::replace(temp.begin(), temp.end(), delims[i], ' ');
}
std::vector<std::pair<int, int> > occurences;
//initialize another input stringstream to make use of the whitespace
std::istringstream ss(temp);
//now use the whitespace to tokenize
while (ss >> temp) {
//try to find the token size in the occurences
std::vector<std::pair<int, int> >::iterator it = std::find_if(occurences.begin(), occurences.end(), CompareFirst(temp.size()));
//if found, increment count by 1
if (it != occurences.end()) {
it->second += 1;//increment the count
}
//this is the first time it has been created. Store value, and a count of 1
else {
occurences.push_back(std::make_pair<int, int>(temp.size(), 1));
}
}
//now sort and output:
std::stable_sort(occurences.begin(), occurences.end(), compare);
for (int i = 0; i < occurences.size(); ++i) {
std::cout << occurences[i].first << " " << occurences[i].second << "\n";
}
std::cout << "\n";
}
return 0;
}
91 строка и все ванильные С++ 98.
Грубая схема того, что я сделал:
РЕКОМЕНДАЦИИ:
Прежде чем считать частоты, вы можете проанализировать входную строку и заменить все символы {".",",","!","?"}
Пробелами (или любым другим символом разделения, который вы хотите использовать). Тогда ваш существующий код должен работать.
Вы можете обрабатывать некоторые символы по-разному. Например, в случае before.Crazy
вы замените '.'
с пространством, но для чего-то вроде 'ny! '
'ny! '
вы бы удалили '!'
в целом, потому что за ним уже следует пробел.