2013-07-02 3 views
3

Я пытаюсь практиковать C++, выполняя некоторые старые проблемы с Google Code Jam. Относительно простой я нашел, чтобы обратить слова в строку. Его можно найти здесь https://code.google.com/codejam/contest/351101/dashboard#s=p1Как я могу оптимизировать этот C++?

До сих пор у меня есть:

#include<iostream> 
using namespace std; 

int main(){ 
    int n = 0; 
    cin >> n; 


    string rev = ""; 
    string buf = ""; 

    string data = ""; 
    getline(cin, data); 

    for(int _ = 0; _ < n; _++){ 
     getline(cin, data); 

     rev = ""; 
     buf = ""; 
     for(char& c : data) { 
      buf += c; 
      if(c == ' '){ 
       rev = buf + rev; 
       buf = ""; 
      } 
     } 

     cout << "Case #" << _ + 1 << ": " << buf << " " << rev << endl; 
    } 

    return 0; 
} 

Который, кажется, бежит довольно быстро. При запуске time ./reverse <in> /dev/null с тестовым файлом около 1.2E6 случаев он занимает около 3.5 секунд при компиляции с g++ -O3.

Так как эталон я создал решение в питона

#!/usr/bin/env python 
from sys import stdin, stdout 
stdout.writelines(map(lambda n: "Case #%d: %s\n" % (n + 1, ' '.join(stdin.readline().split()[::-1])), xrange(int(stdin.readline())))) 

Однако, когда я запускаю его под pypy с time pypy reverse.py <in> /dev/null это занимает около 1.95 секунд.

В теории как pypy написано на C++, не должно быть C++ так быстро или быстро, и если да, как можно оптимизировать этот код для ускорения?

+5

Вы действительно не должны использовать «_» в качестве имени переменной, если не что иное, как вещь стиля, но начинающиеся переменные с _ или __ часто имеют особое значение для некоторых компиляторов. – PherricOxide

+2

@PherricOxide Идентификаторы, начинающиеся с символа подчеркивания, за которым следует буква верхнего регистра, и идентификаторы, содержащие двойные подчеркивания, зарезервированы для реализации. Это относится к _all_ компиляторам. –

+0

Спасибо, что рассказала мне. Я думаю, что я использовал его изначально, потому что я не думал, что мне это нужно, и я думаю, что это просто привычка от кодирования в python, где, если есть переменная, которая вам не нужна в цикле for, которую я нашел, просто назовите ее «_ ». Во всяком случае, изменение его, похоже, не имеет большого значения для времени. – luke

ответ

1

Один простой без копирования/без выделения токенизатор является отвратительным std::strtok

Следующая бьет вашу питона программу в моих тестах

#include <iostream> 
#include <iterator> 
#include <algorithm> 
#include <vector> 
#include <cstring> 

int main() 
{ 
    std::cout.sync_with_stdio(false); // we don't need C in the picture 

    std::string line; 
    getline(std::cin, line); 
    int num_cases = stoi(line); 

    std::vector<char*> words; 
    for(int n = 0; getline(std::cin, line) && n < num_cases; ++n) 
    { 
     words.clear(); 
     char* p = std::strtok(&line[0], " "); 
     while (p) { 
      words.push_back(p); 
      p = std::strtok(nullptr, " "); 
     } 
     std::cout << "Case #" << n + 1 << ": "; 
     reverse_copy(words.begin(), words.end(), 
        std::ostream_iterator<char*>(std::cout, " ")); 
     std::cout << '\n'; // never std::endl! 
    } 
} 

PS: ваш C++ и Python выходы Дон» t соответствует точно; эта программа соответствует вашему выходу на C++

+0

WOW! '1,2' секунд. Это быстро! Я не знал о std :: strtok, но для этого он отлично работает. Благодарю. Кроме того, я думал, что мой код на C++ и Python сделал то же самое, как они отличаются друг от друга? – luke

+1

@ luke python не печатает дополнительное пространство в конце каждой строки – Cubbi

1

Я думаю, что ваш код на C++ делает довольно много копий памяти при конкатенации строк (большинство реализаций std :: string сохраняют всю цепочку непрерывной в памяти.) Я думаю, что следующий код делает это без копий, но я сделал не испытывать его много. Что касается того, почему питон работает достаточно хорошо, я не совсем уверен.

#include<iostream> 

int main() 
{ 
    size_t numCases; 
    std::cin >> numCases; 
    std::cin.ignore(); 

    for(size_t currentCase = 1; currentCase <= numCases; ++currentCase) 
    { 
     std::cout << "Case #" << currentCase << ": "; 

     std::string line; 
     getline(std::cin, line); 
     size_t wordEnd = line.length() - 1; 
     size_t lastSpace = std::string::npos; 
     for (int pos = wordEnd - 1; pos >= 0; --pos) 
     { 
      if (line[pos] == ' ') 
      { 
       for (int prt = pos + 1; prt <= wordEnd; ++prt) 
        std::cout << line[prt]; 
       std::cout << ' '; 
       lastSpace = pos; 
       wordEnd = pos - 1; 
       --pos; 
      } 
     } 
     for (int prt = 0; prt < lastSpace; ++prt) 
      std::cout << line[prt]; 

     std::cout << std::endl; 
    } 

    return 0; 
} 
+0

При компиляции под 'g ++ -O3' это фактически выглядит медленнее при запуске' time ./reverse < in >/dev/null', так как это занимает около 4,5 секунд. – luke

-2

Вместо использования двух буферов и много конкатенации, вы могли бы использовать от алгоритмов и библиотек итераторов, чтобы сделать все это гораздо проще. Я не уверен, насколько быстрее это будет (хотя я бы догадался довольно, но), но он также намного более компактен.

#include<iostream> 
#include<algorithm> 
#include<iterator> 
#include<sstream> 
using namespace std; 

int main(){ 
    int n = 0; 
    cin >> n; 
    string data = ""; 
    getline(cin, data); 
    for(int _ = 0; _ < n; _++){ 
     getline(cin, data); 
     stringstream ss(data); 
     reverse(istream_iterator<string>(ss), istream_iterator<string>()); 
     cout << "Case #" << _ + 1 << ": " << ss.str() << endl; 
    } 
    return 0; 
} 
+0

При попытке скомпилировать код я получаю: 'error: 'ss' не был объявлен в этой области. – luke

+0

#include Извините, что – Kevin

+0

Продолжайте получать эту ошибку: http://pastebin.com/McZX93Ev – luke

Смежные вопросы