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("
");
            }
        }
    }
}
原文地址:https://www.cnblogs.com/EchoZQN/p/14403863.html