• A


    A - DNA Sequencing 后缀数组

    题目大意:

    给你两个字符串,问两个字符串的最长相同子串,如果有多个则按照字典序输出,如果没有则输出"No common sequence.",输出与输出之间空一行,多组输入。

    题解:

    这个因为数据范围很小,所以可以不同后缀数组写,不过刚刚学习了这个算法,所以学习了一下后缀数组的写法。

    这个题目首先要把两个子串进行合并

    • 后缀数组求最长相同子串的长度:因为 (height[i]) 表示的是第 (i) 小的子串和第 (i-1) 小的子串的最长前缀,所以我只需要判断是否存在 (height[i]>0) 并且 (sa[i])(sa[i-1]) 处于两个不同的子串,取这种 (height[i]) 的最大值即是最长相同子串。

      这个可以用二分+哈希的写法来做。

    • 然后就是找子串了,我按照 (rk) 来找,如果 (height[i]>=ans) ,那么我就把这个在1还是2的这个位置放入一个 (set) ,如果找不到这样的 (height[i]>=ans) ,那么就判断一下 (set) 里面是不是两个都有,如果是,那么就存下这个开始的位置。

    注意一下,要把连续 (rk)(height[i]>=ans) 全部遍历完,这个是为了不重复放值。然后因为 (rk) 是从小到大的放的,所以最后的输出结果也一定是按照字典序的。

    这个题目还有一些小小的细节要注意:

    • 为了更好的去分割这两个字符串以及为了简化写法,所以要在第一个和第二个的结尾放入不同的非小写字符来区分。
    #include <bits/stdc++.h>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn = 2e5+10;
    char s[maxn];
    int height[maxn];
    int sa[maxn*2],rk[maxn*2],oldrk[maxn*2],w;
    //sa[i] 表示后缀排序第 i 小的编号,rk[i] 表示后缀 i 的排名
    // 数组开两倍防止 +w 时的越界
    bool cmp(int a,int b){
        return rk[a]==rk[b]?rk[a+w]<rk[b+w]:rk[a]<rk[b];
    }
    int n;
    void getSARK(){
        for(int i=1;i<=2*n;i++) {
            if(i<=n) sa[i] = i,rk[i] = s[i];
            else sa[i] = 0,rk[i] = 0;
            height[i] = 0;
        }
        for(w = 1;w<n;w<<=1){
            sort(sa+1,sa+1+n,cmp);
            for(int j=1;j<=n;j++) oldrk[j] = rk[j];
            for(int p=0,i=1;i<=n;i++){
                if (oldrk[sa[i]] == oldrk[sa[i - 1]] &&
                    oldrk[sa[i] + w] == oldrk[sa[i - 1] + w]) {
                    rk[sa[i]] = p;
                } else {
                    rk[sa[i]] = ++p;
                }  // 若两个子串相同,它们对应的 rk 也需要相同,所以要去重
            }
        }
        for(int i=1,k=0;i<=n;i++){
            if(k) k--;
            while(s[i+k]==s[sa[rk[i]-1]+k]) ++k;
            height[rk[i]] = k;
        }
    }
    char a[maxn],b[maxn];
    int alen,blen,vis[maxn];
    set<int>Set;
    vector<int>Ans;
    int main(){
        int cas = 0;
        while(~scanf("%s%s",a+1,b+1)){
            if(cas) printf("
    ");
            cas++;
            alen = strlen(a+1),blen = strlen(b+1),n = alen + blen + 2;
            for(int i=1;i<=alen;i++) s[i] = a[i],vis[i] = 1;
            s[alen+1] = 'z' + 1,vis[alen+1] = 0;
            for(int i=1;i<=blen;i++) s[i+alen+1] = b[i],vis[i+alen+1] = 2;
            s[n] = 'a' - 1,vis[n] = 0;
            getSARK();
            int ans = 0;
            for(int i=1;i<=n;i++){
                if(vis[sa[i]]+vis[sa[i-1]]==3&&height[i]>ans) {
                    ans = height[i];
                }
            }
            if(!ans) printf("No common sequence.
    ");
            else {
                int fir = 0;
                Set.clear(),Ans.clear();
                for(int i=1;i<=n+1;i++){
                    if(height[i]>=ans){
                        fir = sa[i];
                        Set.insert(vis[sa[i]]);
                        Set.insert(vis[sa[i-1]]);
                    }
                    else{
                        if(Set.size()==2) Ans.push_back(fir);
                        Set.clear();
                    }
                }
                for(int i=0;i<Ans.size();i++){
                    int now = Ans[i];
    //                printf("i = %d now = %d
    ",i,now);
                    for(int j=now;j<=now+ans-1;j++) printf("%c",s[j]);
                    printf("
    ");
                }
            }
        }
    }
    
  • 相关阅读:
    [建议]我对博客园的发展的一点想法
    [转载] 理想、激情、生存—— 一位技术管理人员的20年工作经历和感悟
    FIT FOR .NET(3)
    基于真实项目的TDD应用
    [团队公告]博客园的敏捷软件开发团队成立了
    .NET数据访问体系结构指南
    这是不是微软MSN的一个Bug呢?
    抽象工厂的应用
    简单基础的问题,但是非常容易出错.
    VS 2005 Beta2 Team Suite Edtion
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/14403863.html
Copyright © 2020-2023  润新知