2013-12-28 20:39:04Morris

[数据结构与算法] 第14周 高级数据结构(2)

1:牛奶模式


描述

农场主约翰注意到他家奶牛提供的牛奶质量每天各不相同。通过进一步调查,他发现尽管他不能通过一天的牛奶质量预测下一天,但是每天的牛奶质量存在规律。

为了进行严密的研究,他发明了一个复杂的分类机制,将牛奶样本记录为一个01,000,000(含01,000,000)的整数。而且,他记录了一头奶牛N天(1<=N<=20,000)的数据。他希望找到重复K次(2<=K<=N)的最长的模式。这可能包括重叠的模式——比如1 2 3 2 3 2 3 1 重复了2 3 2 3两次。

请帮助约翰找到样例序列中的最长的重复子序列。数据保证至少存在一个子序列重复了至少K次。


输入
第1行:用空格分隔的两个整数:N和K
第2到N+1行:N个整数,一个一行,第i天的牛奶质量由其中的第i行表示
输出
一行:一个整数,表示重复至少K次的最长模式的长度
样例输入
8 2
1
2
3
2
3
2
3
1
样例输出
4


後綴數組建造高度數組,二分搜尋掃描高度數組即可。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
struct SuffixArray {
    int sa[20005], h[20005], n;
    int w[20005], ta[20005], tb[20005];
    int str[20005];
    void sort(int *x, int *y, int m) {
        static int i;
        for(i = 0; i < m; i++)
            w[i] = 0;
        for(i = 0; i < n; i++)
            w[x[y[i]]]++;
        for(i = 1; i < m; i++)
            w[i] += w[i-1];
        for(i = n-1; i >= 0; i--)
            sa[--w[x[y[i]]]] = y[i];
    }
    bool cmp(int *r, int a, int b, int l) {
        if(r[a] == r[b]) {
            if(a+l >= n || b+l >= n)
                return false;
            return r[a+l] == r[b+l];
        }
        return false;
    }
    void build_h() {
        int i, j, k;
        for(i = 0; i < n; i++)  ta[sa[i]] = i;
        for(i = 0; i < n; i++) {
            if(ta[i] == 0) {
                h[ta[i]] = 0;
                continue;
            }
            if(i == 0 || h[ta[i-1]] <= 1)
                k = 0;
            else
                k = h[ta[i-1]]-1;
            while(str[sa[ta[i]-1]+k] == str[sa[ta[i]]+k])
                k++;
            h[ta[i]] = k;
        }
    }
    void build() {// x: rank, y: second key
        int i, k, m = n, p;
        int *x = ta, *y = tb, *z;
        x[n] = 0;
        for(i = 0; i < n; i++)
            x[i] = str[i], y[i] = i;
        sort(x, y, m);
        for(k = 1, p = 1; p < n; k *= 2, m = p) {
            for(p = 0, i = n-k; i < n; i++)
                y[p++] = i;
            for(i = 0; i < n; i++) {
                if(sa[i] >= k) {
                    y[p++] = sa[i]-k;
                }
            }
            sort(x, y, m);
            z = x, x = y, y = z;
            for(i = 1, p = 1, x[sa[0]] = 0; i < n; i++)
                x[sa[i]] = cmp(y, sa[i-1], sa[i], k) ? p-1 : p++;
        }
    }
};
int main() {
    SuffixArray in;
    int N, K;
    int i, j, k;
    while(scanf("%d %d", &N, &K) == 2) {
        for(i = 0; i < N; i++)
            scanf("%d", &in.str[i]);
        in.n = N;
        std::pair<int, int> d[20005];
        for(i = 0; i < N; i++)
            d[i] = std::make_pair(in.str[i], i);
        std::sort(d, d+N);
        int counter = 0;
        for(i = 0; i < N; i++) {
            if(i == 0 || d[i].first != d[i-1].first)
                counter++;
            in.str[d[i].second] = counter;
        }
        in.build();
        in.build_h();
        int l = 0, r = N, mid;
        int ret = 0;
        while(l <= r) {
            mid = (l+r)/2;
            int flag = 0;
            counter = 0;
            for(i = 0; i < N; i++) {
                if(in.h[i] >= mid)
                    counter++;
                else {
                    if(counter+1 >= K) {
                        flag = 1;
                        break;
                    }
                    counter = 0;
                }
            }
            if(counter+1 >= K)    flag = 1;
            if(flag) {
                l = mid+1;
                ret = mid;
            } else {
                r = mid-1;
            }
        }
        printf("%d\n", ret);
    }
    return 0;
}

2:牛仔裤


描述

基因地理计划是IBM和美国国家地理学会的合作研究项目,用来分析来自于数十万的捐助者的DNA,来研究人类在地球上的迁移图。

作为一个IBM的研究者,你被要求写一个程序,来发现共性的DNA片段,用来和个人调查信息关联以确定新的遗传标记。

DNA碱基序列通过按顺序排列分子中发现的含氮碱基来记录。有四种碱基:腺嘌呤(A),胸腺嘧啶(T),鸟嘌呤(G)和胞嘧啶(C)。一个6碱基的DNA序列可以表示为TAGACC

给定一组DNA的碱基序列,确定发生在所有序列中最长的一系列碱基。


输入
输入以一个整数n作为单独的一行来开头,表示数据集个数。每个数据集包括以下成分:
一个正整数m(2<=m<=10)表示数据集中碱基序列个数
m行,每行包括一个含有60个碱基的序列
输出
对于每一个数据集,输出所有碱基序列中共同的最长的子碱基序列。如果这个最长的共同碱基子序列比三要小,则输出”no significant commonalities”以代替。如果存在多个相同长度的最长共同碱基子序列存在,则只输出按字母顺序排列的第一个序列。
样例输入
3
2
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA
GATACTAGATACTAGATACTAGATACTAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
GATACCAGATACCAGATACCAGATACCAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
3
CATCATCATCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
ACATCATCATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AACATCATCATTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
样例输出
no significant commonalities
AGATAC
CATCATCAT 

後綴數組建造高度數組,二分搜尋掃描高度數組即可。

其中要將所有字串做相接動作,並且每個字串間要填入額外的字元,不可與原本的重複。

O(n log n)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
struct SuffixArray {
    int sa[1005], h[1005], n;
    int w[1005], ta[1005], tb[1005];
    char str[1005];
    void sort(int *x, int *y, int m) {
        static int i;
        for(i = 0; i < m; i++)
            w[i] = 0;
        for(i = 0; i < n; i++)
            w[x[y[i]]]++;
        for(i = 1; i < m; i++)
            w[i] += w[i-1];
        for(i = n-1; i >= 0; i--)
            sa[--w[x[y[i]]]] = y[i];
    }
    bool cmp(int *r, int a, int b, int l) {
        if(r[a] == r[b]) {
            if(a+l >= n || b+l >= n)
                return false;
            return r[a+l] == r[b+l];
        }
        return false;
    }
    void build_h() {
        int i, j, k;
        for(i = 0; i < n; i++)  ta[sa[i]] = i;
        for(i = 0; i < n; i++) {
            if(ta[i] == 0) {
                h[ta[i]] = 0;
                continue;
            }
            if(i == 0 || h[ta[i-1]] <= 1)
                k = 0;
            else
                k = h[ta[i-1]]-1;
            while(str[sa[ta[i]-1]+k] == str[sa[ta[i]]+k])
                k++;
            h[ta[i]] = k;
        }
    }
    void build() {// x: rank, y: second key
        int i, k, m = 128, p;
        int *x = ta, *y = tb, *z;
        x[n] = 0;
        for(i = 0; i < n; i++)
            x[i] = str[i], y[i] = i;
        sort(x, y, m);
        for(k = 1, p = 1; p < n; k *= 2, m = p) {
            for(p = 0, i = n-k; i < n; i++)
                y[p++] = i;
            for(i = 0; i < n; i++) {
                if(sa[i] >= k) {
                    y[p++] = sa[i]-k;
                }
            }
            sort(x, y, m);
            z = x, x = y, y = z;
            for(i = 1, p = 1, x[sa[0]] = 0; i < n; i++)
                x[sa[i]] = cmp(y, sa[i-1], sa[i], k) ? p-1 : p++;
        }
    }
};
int main() {
    SuffixArray in;
    int testcase, n, m;
    int i, j, k;
    char s[128];
    int A[1005];
    scanf("%d", &testcase);
    while(testcase--) {
        scanf("%d", &n);
        int idx = 0;
        for(i = 0; i < n; i++) {
            scanf("%s", s);
            for(j = 0; s[j]; j++) {
                A[idx] = i;
                in.str[idx++] = s[j];
            }
            A[idx] = -1;
            in.str[idx++] = 'a' + i;
        }
        in.str[idx] = '\0';
        in.n = strlen(in.str);
        in.build();
        in.build_h();
        /*printf("%s %d\n", in.str, in.n);
        for(i = 0; i < in.n; i++)
            printf("%s %d\n", in.str+in.sa[i], in.sa[i] >= 0 ? A[in.sa[i]] : -1);*/
        int l = 0, r = in.n, mid;
        int ret = 0, ret_startIdx;
        while(l <= r) {
            mid = (l+r)/2;
            int flag = 0, startIdx;
            int    counter[20] = {}, diff = 0;
            for(i = 0; i <= in.n; i++) {
                if(i != in.n && in.h[i] >= mid) {
                    if(i && A[in.sa[i-1]] >= 0) {   
                        if(counter[A[in.sa[i-1]]] == 0)
                            diff++;
                        counter[A[in.sa[i-1]]]++;
                    }
                } else {
                    if(i && in.h[i-1] >= mid) {
                        if(A[in.sa[i-1]] >= 0) {   
                            if(counter[A[in.sa[i-1]]] == 0)
                                diff++;
                            counter[A[in.sa[i-1]]]++;
                        }                   
                        if(diff == n) {
                            flag = 1;
                            startIdx = in.sa[i-1];
                            break;
                        }
                    }
                    if(diff) {
                        for(j = 0; j < n; j++)
                            counter[j] = 0;
                        diff = 0;           
                    }
                }
            }
            if(flag) {
                l = mid+1;
                ret = mid;
                ret_startIdx = startIdx;
                //printf("search %s %d\n", in.str + startIdx, ret);
            } else {
                r = mid-1;
            }
        }
        if(ret < 3)
            puts("no significant commonalities");
        else {
            for(i = 0; i < ret; i++)
                printf("%c", (in.str + ret_startIdx)[i]);
            puts("");
        }
    }
    return 0;
}
/*
3
2
GATACCAGATACCA
AAAAAAAAAAAAAA
3
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA
GATACTAGATACTAGATACTAGATACTAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
GATACCAGATACCAGATACCAGATACCAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
*/