3145: [Feyat cup 1.5]Str

3145: [Feyat cup 1.5]Str

Time Limit: 40 Sec   Memory Limit: 512 MB
Submit: 142   Solved: 25
[ Submit][ Status][ Discuss]

Description

Arcueid,白姬,真祖的公主。在和推倒贵看电影时突然对一个问题产生了兴趣:
我们都知道真祖和死徒是有类似的地方。那么从现代科学的角度如何解释
呢?自然就得研究遗传密码了。Arcueid得知了两者的DNA片段,想寻求一个
DNA片段,使得其在两者的DNA中都出现过。
我们知道公主的脑袋有点不太灵活,如果两个DNA片段只有一个位置不
同,她也会将其认为是相同的。所以请您找出这样的最长的DNA片段吧。

Input

两行,每行一个字符串。

Output

一个整数,表示最长的 DNA 片段的长度。


Sample Input

aabbe
acbbc



Sample Output

4

HINT

100% 的数据 n<=10^5;m<=10^5 。

每个串中只包含小写字母(别问我为什么不是 ATCG )。

Source

[ Submit][ Status][ Discuss]

假设最优方案中,A串第i个位置和B串第j个位置被选为不等的位置
那么,对(i-1,j-1)求个lcs,对(i+1,j+1)求个lcp,这样三部分结合就是答案了
可以枚举点对(i,j),然后用hash...bulabulabula...O(n^2logn)

对于两个串,分别在它们的头尾都填上一个奇怪的字符,一共四个,四个都不一样
然后将两个串接起来,求个sam,这样,sam每个点的right集里的点的组合,lcp就是当前点的len
对于拼接串,再求个sa,得到height
为了求lcs,不妨每个点打个标记,就是它所属位置+2的rank(这就是为什么添加奇怪字符)
最后,在sam上用线段树合并,不断更新即可
就是线段树维互right集合内任意两点组合的lcs最大值
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;
 
const int maxn = 2E5 + 20;
const int maxm = maxn * 2;
const int T = 20;
const int INF = ~0U>>1;
 
int n1,n2,N,Ans,cnt,tot,last,Cnt,root,height[maxn],sa[maxn],rank[maxn],t[maxn],t2[maxn],c[maxn],ch[maxm][30],LEN[maxm],fail[maxm]
    ,rt[maxm],lc[maxn*T],rc[maxn*T],Left[maxn*T][2],Right[maxn*T][2],Max[maxn*T],Min[maxn][T],bin[maxn],len[maxn],pos[maxn];
char s[maxn],A[maxn],B[maxn];
 
vector <int> v[maxm];
 
void Work(int *height,int *sa,int *rank)
{
    int *x = t,*y = t2,m = 30;
    for (int i = 1; i <= m; i++) c[i] = 0;
    for (int i = 1; i <= N; i++) ++c[x[i] = s[i] + 1];
    for (int i = 1; i <= m; i++) c[i] += c[i-1];
    for (int i = N; i; i--) sa[c[x[i]]--] = i;
    for (int k = 1; k < N; k <<= 1)
    {
        int p = 0;
        for (int i = N; i > N - k; i--) y[++p] = i;
        for (int i = 1; i <= N; i++) if (sa[i] > k) y[++p] = sa[i] - k;
        for (int i = 1; i <= m; i++) c[i] = 0;
        for (int i = 1; i <= N; i++) ++c[x[i]];
        for (int i = 2; i <= m; i++) c[i] += c[i-1];
        for (int i = N; i; i--) sa[c[x[y[i]]]--] = y[i];
        swap(x,y); p = 1; x[sa[1]] = 1;
        for (int i = 2; i <= N; i++)
            x[sa[i]] = y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k] ? p : ++p;
        m = p; if (m == N) break;
    }
     
    int k = 0;
    for (int i = 1; i <= N; i++) rank[sa[i]] = i;
    for (int i = 1; i <= N; i++)
    {
        if (k) --k;
        int j = sa[rank[i] - 1];
        while (s[i + k] == s[j + k]) ++k;
        height[rank[i]] = k;
    }
    /*for (int i = 1; i <= N; i++) putchar(s[i] + 'a'); puts("");
    for (int i = 1; i <= N; i++)
    {
        for (int j = sa[i]; j <= N; j++) putchar(s[j] + 'a');
        printf(" %d\n",height[i]);
    }*/
}
 
int Extend(int Nex,int le)
{
    int p = last,np = ++Cnt; last = np; LEN[np] = le;
    while (p && !ch[p][Nex])
        ch[p][Nex] = np,p = fail[p];
    if (!p) {fail[np] = root; return np;}
     
    int q = ch[p][Nex];
    if (LEN[q] - LEN[p] == 1) fail[np] = q;
    else
    {
        int nq = ++Cnt; LEN[nq] = LEN[p] + 1;
        memcpy(ch[nq],ch[q],sizeof(ch[q]));
        fail[nq] = fail[q]; fail[q] = fail[np] = nq;
        while (p && ch[p][Nex] == q)
            ch[p][Nex] = nq,p = fail[p];
    }
    return np;
}
 
int Insert(int l,int r,int pos,int typ)
{
    int ret = ++cnt; Max[ret] = -INF;
    Left[ret][typ] = Right[ret][typ] = pos;
    Left[ret][typ^1] = INF; Right[ret][typ^1] = 0;
    if (l == r) return ret; int mid = (l + r) >> 1;
    if (pos <= mid) lc[ret] = Insert(l,mid,pos,typ);
    else rc[ret] = Insert(mid+1,r,pos,typ);
    return ret;
}
 
int RMQ(int l,int r)
{
    ++l; int k = r - l + 1;
    return min(Min[l][bin[k]],Min[r - len[k] + 1][bin[k]]);
}
 
int Merge(int o1,int o2,int l,int r)
{
    if (!o1) return o2; if (!o2) return o1;
    int mid = (l + r) >> 1; Max[o1] = -INF;
    lc[o1] = Merge(lc[o1],lc[o2],l,mid);
    rc[o1] = Merge(rc[o1],rc[o2],mid+1,r);
    for (int i = 0; i < 2; i++)
    {
        Left[o1][i] = min(Left[lc[o1]][i],Left[rc[o1]][i]);
        Right[o1][i] = max(Right[lc[o1]][i],Right[rc[o1]][i]);
    }
    Max[o1] = max(Max[lc[o1]],Max[rc[o1]]);
    if (lc[o1] && rc[o1])
    {
        if (Right[lc[o1]][0] != 0 && Left[rc[o1]][1] != INF)
            Max[o1] = max(Max[o1],RMQ(Right[lc[o1]][0],Left[rc[o1]][1]));
        if (Right[lc[o1]][1] != 0 && Left[rc[o1]][0] != INF)
            Max[o1] = max(Max[o1],RMQ(Right[lc[o1]][1],Left[rc[o1]][0]));
    }
    return o1;
}
 
void Dfs(int x)
{
    for (int i = 0; i < v[x].size(); i++)
    {
        int to = v[x][i]; Dfs(to);
        rt[x] = Merge(rt[x],rt[to],1,N);
    }
    if (Max[rt[x]] != -INF) Ans = max(Ans,Max[rt[x]] + LEN[x]);
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    scanf("%s",A + 1); n1 = strlen(A + 1); scanf("%s",B + 1); n2 = strlen(B + 1);
    s[tot = 1] = 26; for (int i = 1; i <= n1; i++) s[++tot] = A[i] - 'a';
    s[++tot] = 27; s[++tot] = 28; for (int i = 1; i <= n2; i++) s[++tot] = B[i] - 'a';
    s[++tot] = 29; s[0] = s[tot+1] = 30; N = n1 + n2 + 4; Work(height,sa,rank);
    root = last = Cnt = 1; for (int i = 1; i <= N; i++) pos[i] = Extend(s[i],i);
    for (int i = 1; i <= N; i++)
    {
        bin[i] = (1 << bin[i-1] + 1) < i ? bin[i-1] + 1 : bin[i-1];
        len[i] = (1 << bin[i]); Min[i][0] = height[i];
    }
    for (int j = 1; j < T; j++)
        for (int i = 1; i <= N; i++)
        {
            int Nex = i + (1 << j - 1); if (Nex > N) break;
            Min[i][j] = min(Min[i][j-1],Min[Nex][j-1]);
        }
         
    Left[0][0] = Left[0][1] = INF; Max[0] = -INF;
    for (int i = 2; i <= Cnt; i++) v[fail[i]].push_back(i);
    for (int i = 2; i <= n1 + 1; i++)
        rt[pos[i-1]] = Insert(1,N,rank[i+1],0);
    for (int i = 2; i <= n2 + 1; i++)
        rt[pos[i-1+n1+2]] = Insert(1,N,rank[i+1+n1+2],1);
    Dfs(root); cout << Ans + 1 << endl;
    return 0;
}

对于拼接串,再求个sa,得到height
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值