【问题标题】:Finding all substrings within a string and storing the results?查找字符串中的所有子字符串并存储结果?
【发布时间】:2015-05-10 14:53:07
【问题描述】:
class ORF_Finder {
public:
    void findORFs(string & strand, int sizeOfStrand); 
    vector<string> orf1Strands; 
    vector<string> orf2Strands;
    vector<string> orf3Strands; 
private:
    string newStrand;
    string newSub;
};    


void ORF_Finder::findORFs(string & strand, int sizeOfStrand) {

        int pos, pos1, index = 0;

for (int i = 0; i < strand.size(); i++) {
        pos = strand.find("ATG"); 
        pos1 = strand.find("TAA"); 
        newSub = strand.substr(pos, pos1);
        newStrand.insert(index, newSub);
        strand.erase(pos, pos1);
        index = index + 3; 

        if ((pos1 % 3 == 0) && (pos1 >= pos + 21)) {
            orf1Strands.push_back(newStrand); 
        }

        else if ((pos1 % 3 == 1) && (pos1 >= pos + 21)) { 
            orf2Strands.push_back(newStrand);
        }

        else if ((pos1 % 3 == 2) && (pos1 >= pos + 21)) {
            orf3Strands.push_back(newStrand); 
        }
   }
}

^ 假设所有字符串都已声明,并且我正在“使用命名空间 std”。

我的目标是向用户询问导入的 DNA 链(例如:“TCAATGCGCGCTACCATGCGGAGCTCTGGGCCCAAATTTCATCCATAACTGGGGCCCTTTTAAGGGCCCGGGAAATTT”)并查找子字符串以“ATG”开头并以“TAA”、“TAG”或“TGA”结尾的所有实例(为简单起见,我省略了“TAG”和“TGA”)。

子字符串将是这样的:“ATG ... ... ... ... TAA”,然后将其存储到向量中以供以后使用。但是,我想找到每个阅读框的多个实例(ORF1应该从导入链的“T”开始,ORF2应该从导入链的“C”开始,ORF3应该从“A”开始导入的链)并且应该以三元组形式工作,因此在 if 语句中包含 mod 3。 “pos1 >= pos + 21”的目的是使每个子串至少有七个密码子长。

上面的代码是我迄今为止所做的,但很明显,它是不正确的。我试图告诉 pos 找到“ATG”,让 pos1 找到“TAA”。 newSub 是将从“ATG”生成到“TAA”的子字符串,并且将生成 newStrand 以包含该子字符串。然后我会擦除链的一部分(以避免重复)并增加索引。

抱歉,描述太长了,但我一直在强调这个问题,我已经尽我的意志力尝试了一切来解决这个问题。

【问题讨论】:

  • 如果子字符串重叠,您想做什么:ATG...ATG...TAA...TAA?你消费了从 ATG 到第一个 TAA 的所有内容,并在 TAA 之后搜索另一个 ATG ?
  • @SergeBallesta 对不起,我提供了一个糟糕的例子。应该是这样的:TCAATGCGCGCTACCCGGAGCTCTGGGCCCAAATTTCATCCTAAACT。本质上,不应该有重叠的子字符串,并且序列会更长。
  • 也许您应该将 DNA 序列存储在特定的数据结构中,而不是字符串中。字符串将整个字母表考虑在内,导致内存使用量增加 4 倍...
  • 查看此SO Question 以有效选择数据结构。如果你打算继续使用字符串,KMP 是你应该使用的算法。

标签: c++ string


【解决方案1】:

Knutt-Morris Pratt 是最快的解决方案。 Aho-corasick 算法是 kmp 算法的广义版本。基本上它是从广度优先搜索计算的失败链接的尝试。你可以试试我的 PHP 实现 phpahocorasick@codeplex.com。然后你需要添加一个通配符来查找所有子字符串。

【讨论】:

    【解决方案2】:

    这是一个可能的实现。

    特点:

    • 可以处理大字符串,因为它只保留初始字符串的一个副本
    • 接受一个任意初始序列(此处为“ATG”)
    • 接受许多末端序列(此处为“TAA”、“TAG”或“TGA”)
    • 只接受至少 7 个密码子的子串
    • 子字符串仅由初始字符串中的索引和长度来描述(以节省内存)
    • 根据您的要求,根据索引的模 3 将结果保留在 3 个不同的向量中

    代码:

    #include <iostream>
    #include <string>
    #include <stdexcept>
    #include <vector>
    
    class Strand {
        const std::string* data;
        size_t begin;
        size_t len;
    
    public:
        Strand(const std::string& data, size_t begin, size_t end): begin(begin),
            len(end - begin), data(&data) {
                if (end <= begin) {
                    throw std::invalid_argument("end < begin");
                }
        }
        std::string getString() const {
            const char *beg = data->c_str();
            beg += begin;
            return std::string(beg, len);
        }
    };
    
    class Parser {
        const std::string& data;
        const std::string& first;
        const std::vector<std::string>& end;
        size_t dataLen;
        std::vector<Strand> orf1Strands;
        std::vector<Strand> orf2Strands;
        std::vector<Strand> orf3Strands;
    
    public:
        enum TypStrand {
            one = 0, two, three
        };
        Parser(const std::string& data, const std::string& first,
            const std::vector<std::string>& end): data(data),
            first(first), end(end) {
                dataLen = data.length();
        }
    
        void parse();
        const std::vector<Strand>& getVector(int typ) const {
            switch(typ) {
                case 0 : return orf1Strands;
                case 1 : return orf2Strands;
                default : return orf3Strands;
            }
        }
        const std::vector<Strand>& getVector(TypStrand typ) const {
            return getVector((int) typ);
        }
    
    };
    
    void Parser::parse() {
        size_t pos=0;
        size_t endSize = end.size();
        std::string firstChars = "";
        for(size_t i=0; i<endSize; i++) {
            firstChars += end[i].at(0);
        }
    
        for(;;) {
            pos = data.find(first, pos);
            if (pos == std::string::npos) break;
            size_t strandEnd = pos + 18;
            for(;;) {
                if (strandEnd + 3 >= dataLen) break;
                strandEnd = data.find_first_of(firstChars, strandEnd);
                if ((strandEnd - pos) % 3 != 0) {
                    strandEnd += 1;
                    continue;
                }
                if (strandEnd + 3 >= dataLen) break;
                for (size_t i=0; i<endSize; i++) {
                    if (data.compare(strandEnd, end[i].length(), end[i]) == 0) {
                        std::cout << "Found sequence ended with " << end[i] << std::endl;
                        switch(pos %3) {
                            case 0 :
                                orf1Strands.push_back(Strand(data, pos,
                                    strandEnd + 3));
                                break;
                            case 1 :
                                orf2Strands.push_back(Strand(data, pos,
                                    strandEnd + 3));
                                break;
                            case 2 :
                                orf3Strands.push_back(Strand(data, pos,
                                    strandEnd + 3));
                                break;
                        }
                        pos = strandEnd + end[i].length() - 1;
                        break;
                    }
                }
                if (pos > strandEnd) break;
                strandEnd += 3;
            }
            if (strandEnd + 3 >= dataLen) break;
            pos = pos + 1;
        }
    }
    
    using namespace std;
    
    int main() {
        std::string first = "ATG";
        vector<string> end;
        std::string ends[] = { "TAA", "TAG", "TGA"};
        for (int i=0; i< sizeof(ends)/sizeof(std::string); i++) {
            end.push_back(ends[i]);
        }
    
        string data = "TCAATGCGCGCTACCATGCGGAGCTCTGGGCCCAAATTTC"
            "ATCCATAACTGGGGCCCTTTTAAGGGCCCGGGAAATTT";
    
        Parser parser(data, first, end);
        parser.parse();
    
        for (int i=0; i<3; i++) {
            int typ = i;
            const vector<Strand>& vect = parser.getVector(typ);
            cout << "Order " << i << " : " << vect.size() << endl;
            if (vect.size() > 0) {
                for(size_t j=0; j<vect.size(); j++) {
                    cout << vect[i].getString() << endl;
                }
            }
        }
        return 0;
    }
    

    待办事项:

    • 添加 cmets
    • 修复 enum TypStrand 的管理:程序编写完成后,我认为拥有三个向量的数组比三个单独的向量更好。
    • 应配置最少数量的密码子
    • 针对极端情况进行更密集的测试
    • 3 是一个幻数,应该用常数表示

    【讨论】:

    • 感谢您的评论。谢谢你。当然,我只用 C++ 编程了大约六个月,所以我对这一切都比较陌生,但我会用你的代码作为学习工具。再次感谢。
    【解决方案3】:

    简单:

    1. 扫描整个字符串以查找开始或结束序列的出现。
    2. 如果找到结束序列,请从前一个开始序列中提取部分。

    您将有一些角落案例,例如处理可能以不同方式配对的多个信号序列,但这只是正常的编程。

    您的方法的问题是您没有从头到尾扫描字符串,而是从头开始反复搜索开头和结尾。您需要在最后一个位置之后继续。查看string 类的各种find.. 函数,了解如何执行此操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-01
      • 1970-01-01
      • 2017-08-05
      • 1970-01-01
      • 1970-01-01
      • 2012-10-24
      • 1970-01-01
      相关资源
      最近更新 更多