0

我有以下排序数据:

AAA
AAA
TCG
TTT
TTT
TTT

我想计算每个字符串的出现次数:

AAA 2
TCG 1
TTT 3

我知道我可以使用 来做到这一点uniq -c,但在这里我需要对我拥有的整个 C++ 代码进行额外处理。

我坚持这个结构(根据'pgras'建议修改):

#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
using namespace std;


int main  ( int arg_count, char *arg_vec[] ) {
    if (arg_count !=2 ) {
        cerr << "expected one argument" << endl;
        return EXIT_FAILURE;
    }

    string line;
    ifstream myfile (arg_vec[1]);


    if (myfile.is_open())
    {
        int count;
        string lastTag = "";

        while (getline(myfile,line) )
        {
            stringstream ss(line);
            string Tag;

            ss >> Tag; // read first column
            //cout << Tag << endl; 

            if (Tag != lastTag) {
               lastTag = Tag;
               count = 0;
            }
            else {
                count++;
            }

             cout << lastTag << " " << count << endl;
        }
        cout << lastTag << " " << count << endl;
        myfile.close();

    }
    else {cout << "Unable to open file";}
    return 0;
}

它打印出这个错误的结果:

AAA 0
AAA 1
TCT 0
TTT 0
TTT 1
TTT 2
TTT 2
4

8 回答 8

6

如果你只是想打印出来,你的算法是可以的。如果你想将它传递给另一个函数,你可以使用例如 STL 映射。

map<string, int> dict;

while(getline(myfile,line)) {
          string Tag;
          stringstream ss(line);
          ss >> Tag;
          if (dict.count(Tag) == 0) 
               dict[Tag] = 1;
           else
               dict[Tag]++;
}    
于 2009-03-11T09:48:58.437 回答
6

当标签与 lastTag 不同时,您必须重置计数器,如果相同则增加...当标签不同时,您可以使用其关联的计数值处理前一个标签(在重置计数之前)...

于 2009-03-11T09:49:33.117 回答
4

使用这样的东西:

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <map>
#include <iterator>


std::ostream& operator << ( std::ostream& out, const std::pair< std::string, size_t >& rhs )
{
    out << rhs.first << ", " << rhs.second;
    return out;
}

int main() 
{
    std::ifstream inp( "mysorted_data.txt" );
    std::string str;
    std::map < std::string, size_t > words_count;
    while ( inp >> str )
    {
        words_count[str]++;
    }

    std::copy( 
        words_count.begin(), 
        words_count.end(), 
        std::ostream_iterator< std::pair< std::string, size_t > >( std::cout, "\n" ) );

    return 0;
}
于 2009-03-11T10:25:48.850 回答
4

假设您的数据确实包含长度为 3 的 DNA 字符串(或更一般的长度N,其中N非常小),您可以通过使用 q-gram 表来提高效率,该表是表大小为 4 的专用哈希表N和以下散列函数:

// Disregard error codes.
int char2dna_lookup[] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0  – 0xF
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 – 0x1F
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20 – 0x2F
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x30 – 0x3F
    0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A    – P
    0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Q    – 0x5F
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 – 0x6F
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x70 – 0x7F
}

unsigned int hash(string const& dna) {
    unsigned int ret = 0;

    for (unsigned int i = 0; i < dna.length(); ++i)
        ret = ret * 4 + char2dna_lookup[dna[i]];

    return ret;
}

您现在可以非常有效地索引您的数组。

ifstream ifs("data.txt");
string line;

if (not ifs >> line)
    exit(1);

unsigned* frequencies = new unsigned int[line.length()];

frequencies[hash(line)] = 1;

while (ifs >> line)
    ++frequencies[hash(line)];

// Print the frequencies …

delete[] frequencies;

或者,为此类任务使用诸如SeqAn 之类的库。

于 2009-03-11T10:33:50.903 回答
2

我认为你所要做的就是替换这个

        if (Tag != lastTag) {
           lastTag = Tag;
           count = 0;
        }
        else {
            count++;
        }

        cout << lastTag << " " << count << endl;

有了这个:

        if (Tag != lastTag) {
            if (lastTag != "") {  // don't print initial empty tag
                cout << lastTag << " " << count << endl;
            }
            lastTag = Tag;
            count = 1; // count current
          } else {
            count++;
        }
于 2009-03-11T16:23:12.280 回答
1

您的代码在语法上看起来有些损坏(ifstream...),但我认为整体算法是合理的。读取行,并在每次该行与之前的行相同时增加一个计数器。可能需要考虑一些边界条件(如果输入只有一行怎么办?),但您会在测试期间发现这些条件。

于 2009-03-11T09:41:13.730 回答
1

使用 stringstream 来获取标签似乎有点矫枉过正——我可能会使用 string::substr。除此之外,您认为您的代码有什么问题?你想改进什么?

编辑:接下来,我们将因呼吸而被否决……

于 2009-03-11T09:43:58.683 回答
1
#include <map>
#include <string>
#include <algorithm>
#include <iterator>
#include <iostream>

class Counter
{   private: std::map<std::string,int>&   m_count;
    public:  Counter(std::map<std::string,int>& data) :m_count(data){}
        void operator()(std::string const& word)
        {
            m_count[word]++;
        }};
class Printer
{   private: std::ostream& m_out;
    public:  Printer(std::ostream& out) :m_out(out) {}
        void operator()(std::map<std::string,int>::value_type const& data)
        {
            m_out << data.first << " = " << data.second << "\n";
        }};

int main()
{
    std::map<std::string,int>       count;

    for_each(std::istream_iterator<std::string>(std::cin),
             std::istream_iterator<std::string>(),
             Counter(count)
            );

    for_each(count.begin(),count.end(),
             Printer(std::cout)
            );
}
于 2009-03-11T18:52:25.183 回答