2015深圳杯數學建模-DNA序列k-mer index問題



這是一個數學建模的問題,非常考察編程能力。

我們選擇這個題目,就是因爲i這個題目可以轉化爲模式串匹配問題。

由於這個題目中的模式串爲k-mer,數量特別大,我們隊用了多模式匹配的AC自動機算法找位置。

但是存儲k-mer位置的索引表的數據量也很大,我們就用了哈希函數的直接定址法+鏈表的方式建立索引表。

雖然在海量數據下,建立索引的時間很長,但是一旦建立索引再去查找位置非常快。

題目如下:

這個問題來自 DNA序列的k-mer index問題。

給定一個DNA序列,這個系列只含有4個字母ATCG,如 S =“CTGTACTGTAT”。給定一個整數值k,從S的第一個位置開始,取一連續k個字母的短串,稱之爲k-mer(如k= 5,則此短串爲CTGTA), 然後從S的第二個位置, 取另一k-mer(如k= 5,則此短串爲TGTAC),這樣直至S的末端,就得一個集合,包含全部k-mer 。 如對序列S來說,所有5-mer爲

{CTGTA,TGTAC,GTACT,TACTG,ACTGT,TGTAT}

通常這些k-mer需一種數據索引方法,可被後面的操作快速訪問。例如,對5-mer來說,當查詢CTGTA,通過這種數據索引方法,可返回其在DNA序列S中的位置爲{1,6}。

問題

現在以文件形式給定 100萬個 DNA序列,序列編號爲1-1000000,每個基因序列長度爲100 。

(1)要求對給定k, 給出並實現一種數據索引方法,可返回任意一個k-mer所在的DNA序列編號和相應序列中出現的位置。每次建立索引,只需支持一個k值即可,不需要支持全部k值。

(2)要求索引一旦建立,查詢速度儘量快,所用內存儘量小。

(3)給出建立索引所用的計算複雜度,和空間複雜度分析。

(4)給出使用索引查詢的計算複雜度,和空間複雜度分析。

(5)假設內存限制爲8G,分析所設計索引方法所能支持的最大k值和相應數據查詢效率。

(6)按重要性由高到低排列,將依據以下幾點,來評價索引方法性能

· 索引查詢速度

· 索引內存使用

· 8G內存下,所能支持的k值範圍

· 建立索引時

我們隊高大上的代碼:

#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
const long long maxn = 1048580;
const int INF = 0x3fffff;
const double EPS = 1e-6;
const double PI = 3.1415926;
typedef  long long ll;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};

int k;
typedef struct node    //儲值
{
    int add;
    struct node *next;
} node,*Rlink;
Rlink head[maxn];
void List_insert(int add,int data)                  //鏈表中插入地址
{
    if(head[add]==NULL)
    {
        head[add] = (node *)malloc(sizeof(node));
        head[add]->add = data;
        head[add]->next = NULL;
    }
    else
    {
        node *p = head[add];
        while(p->next)
            p=p->next;
        node *q = (node *)malloc(sizeof(node));
        q->add = data;
        q->next = NULL;
        p->next = q;
    }
}

char str[1048580][101];
char lstr[110000000];
int hash(char *strr);

struct Trie
{
    int next[1048580][101],fail[1048580],end[1048580];
    int root,L;
    int newnode()
    {
        for(int i = 0; i < 101; i++)
            next[L][i] = -1;
        end[L++] = -1;
        return L-1;
    }
    void init()
    {
        L = 0;
        root = newnode();
    }
    void insert(char s[],int id)
    {
        int len = strlen(s);
        int now = root;
        for(int i = 0; i < len; i++)
        {
            if(next[now][s[i]] == -1)
                next[now][s[i]] = newnode();
            now = next[now][s[i]];
        }
        end[now] = id;
    }
    void build()
    {
        queue<int>Q;
        fail[root] = root;
        for(int i = 0; i < 101; i++)
            if(next[root][i] == -1)
                next[root][i] = root;
            else
            {
                fail[next[root][i]] = root;
                Q.push(next[root][i]);
            }
        while(!Q.empty())
        {
            int now = Q.front();
            Q.pop();
            for(int i = 0; i < 101; i++)
                if(next[now][i] == -1)
                    next[now][i]=next[fail[now]][i];
                else
                {
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
        }
    }
    int num[1048580];//記錄個數
    vector<int>post[1048580];//記錄位置

    void query(char buf[],int n)
    {
        for(int i = 0; i < n; i++)
            num[i] = 0,post[i].clear();
        int len=strlen(buf);
        int now=root;
        for(int i=0; i<len; i++)
        {
            now=next[now][buf[i]];
            int temp = now;
            while( temp != root )
            {
                if(end[temp] != -1)
                {
                    num[end[temp]]++;
                    post[end[temp]].push_back(i);
                }
                temp = fail[temp];
            }
        }
        for(int i = 0; i < n; i++)
        {
            if(num[i] > 0)
            {
                int add = hash(str[i]);    //存儲在數組中
                for(int j=0; j<post[i].size(); j++)
                {
                    List_insert(add,post[i][j]+1-k);      //
                }
            }
        }
    }
};
Trie ac;

int hash(char *strr)
{
    int len = strlen(strr);
    int add = 0;  //地址
    int base = 1; //進制
    for(int i=len-1; i>=0; i--)
    {
        switch(strr[i])
        {
        case 'A':
            add += 0*base;
            break;
        case 'T':
            add += 1*base;
            break;
        case 'G':
            add += 2*base;
            break;
        case 'C':
            add += 3*base;
            break;
        }
        base*=4;
    }
    return add;
}

void substr(char *st,char *ss,int i,int j)  //i開始截取j個字符,扔進st中
{
    for(int k=0; k<j; k++)
        st[k] = ss[k+i];
    st[j] = '\0';
}

void Index(char *s)
{
    int add = hash(s);
    node *p = head[add];
    while(p)
    {
        printf("模式串所在DNA序列號爲: %d,所在的下標爲: %d\n",p->add/101+1,p->add%101+1);
        p=p->next;
    }
}

int main()
{
    printf("請輸入一個K值:\n");
    scanf("%d",&k);
    map <string,int> map;       //去重
    string ss;
    char tmp[102];
    ac.init();
    int i=0;
    int cnt=0;   //記錄模式串個數
    ifstream in("test.txt");  //文件讀取
    if(in)
    {
        while(getline(in,ss))
        {
            ss.copy(tmp,100,0);
            tmp[100] = '*';          //將不同行的字符串分隔開,避免跨行匹配成功
            tmp[101] = '\0';
            strcat(lstr,tmp);
            for(int j=0; j<=100-k; j++)
            {
                string moshi = ss.substr(j,k);
                if(map[moshi]==1) continue;       //已經插入過的模式串不會再插入
                substr(str[cnt],tmp,j,k);
                ac.insert(str[cnt],cnt);
                cnt++;
                map[moshi] = 1;                   //插入標記
            }
            i++;
        }
    }
    ac.build();   //ac自動機建立
    ac.query(lstr,cnt);
    char l[101];
    printf("請輸入您要查詢的k-mer:\n");
    while(~scanf("%s",l))
        Index(l);
    return 0;
}






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章