1. 程式人生 > >【[TJOI2017]DNA】

【[TJOI2017]DNA】

[題目][https://www.lydsy.com/JudgeOnline/problem.php?id=4892]

好像用\(SAM\)做的都是\(dfs\)

其實這裡也是搜尋

如果用\(SAM\)來做非常好理解,就是從\(SAM\)上匹配這個字串,允許有不超過三條轉移邊不一樣

於是\(dfs\)做法非常顯然了,就是爆搜這三條不一樣的轉移邊在哪裡,但是複雜度看起來好像很迷,可能是\(\binom{n}{3}\)也就是\(O(n^3)\)級別

但是由於很多情況下選擇某一條轉移甚至都無法在\(SAM\)上有一個長度為\(n\)的匹配,所以\(dfs\)非常的快

這裡寫的是一個\(bfs\)

,但是長得和\(dp\)有點像?

\(f[i][j][k]\)表示在\(SAM\)上跑了\(i\)位跑到了\(j\)這個節點,\(k\)次個位置不一樣是否可行,顯然的轉移就是列舉下一個是走\(A,C,G,T\)

發現這個陣列可以滾動,之後就滾動好了

滾動的同時還需要滾動兩個棧,用來儲存有用的狀態,其實就是廣搜裡的隊列了

還有一個小剪枝,我們提前處理出每一個節點往下跑出來的最長路,我們可以根據這個快速判斷出一個狀態是否可行

程式碼

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 200005
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
int Test,lst,cnt,num,n,top[2],tot,o;
struct St {int a,b;} st[2][maxn];
struct E{int v,nxt;} e[maxn<<1];
char S[maxn>>1],T[maxn>>1];
int son[maxn][4],fa[maxn],sz[maxn],len[maxn],head[maxn],mx[maxn],q[maxn],r[maxn],f[maxn][4];
inline void add(int x,int y) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;}
void dfs(int x) {for(re int i=head[x];i;i=e[i].nxt) dfs(e[i].v),sz[x]+=sz[e[i].v];}
inline int solve(char c) {if(c=='A') return 0;if(c=='C') return 1;if(c=='G') return 2;return 3;}
inline void ins(int c)
{
    int f=lst,p=++cnt; lst=p;
    len[p]=len[f]+1,sz[p]=1;
    while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
    if(!f) {fa[p]=1;return;}
    int x=son[f][c];
    if(len[f]+1==len[x]) {fa[p]=x;return;}
    int y=++cnt;
    len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y;
    for(re int i=0;i<4;i++) son[y][i]=son[x][i];
    while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
}
int main()
{
    scanf("%d",&Test);
    while(Test--)
    {
        scanf("%s",S+1);scanf("%s",T+1);
        for(re int i=1;i<=cnt;i++) 
            r[i]=head[i]=mx[i]=len[i]=fa[i]=sz[i]=son[i][0]=son[i][1]=son[i][2]=son[i][3]=0;
        top[0]=top[1]=0;
        lst=cnt=1,num=0;
        n=strlen(S+1);
        for(re int i=1;i<=n;i++) S[i]=solve(S[i]),ins(S[i]);
        for(re int i=2;i<=cnt;i++) add(fa[i],i); dfs(1);
        num=tot=o=0; 
        for(re int i=1;i<=cnt;i++) head[i]=0;
        for(re int i=1;i<=cnt;i++)
            for(re int j=0;j<4;j++) if(son[i][j]) add(son[i][j],i),r[i]++;
        for(re int i=1;i<=cnt;i++) if(!r[i]) q[++tot]=i;
        for(re int i=1;i<=tot;i++)
            for(re int j=head[q[i]];j;j=e[j].nxt) 
            {r[e[j].v]--; mx[e[j].v]=max(mx[e[j].v],mx[q[i]]+1);if(!r[e[j].v]) q[++tot]=e[j].v;}
        n=strlen(T+1);
        for(re int i=1;i<=n;i++) T[i]=solve(T[i]);
        memset(f,0,sizeof(f));
        st[0][++top[0]]=(St){1,0};
        for(re int i=1;i<=n;i++,o^=1)
        {
            for(re int j=1;j<=top[o];j++)
                f[st[o][j].a][st[o][j].b]=0;
            top[o^1]=0;
            for(re int j=1;j<=top[o];j++)
            {
                int x=st[o][j].a,y=st[o][j].b;
                for(re int k=0;k<4;k++) 
                if(son[x][k]&&k!=T[i]&&y<3) 
                {
                    if(mx[son[x][k]]+i<n) continue;
                    if(!f[son[x][k]][y+1]) st[o^1][++top[o^1]]=(St){son[x][k],y+1},f[son[x][k]][y+1]=1;
                }
                int k=T[i];
                if(!son[x][k]||mx[son[x][k]]+i<n) continue;
                if(!f[son[x][k]][y]) st[o^1][++top[o^1]]=(St){son[x][k],y},f[son[x][k]][y]=1;
            }
        }
        int ans=0;
        for(re int i=2;i<=cnt;i++) if(f[i][0]||f[i][1]||f[i][2]||f[i][3]) ans+=sz[i];
        printf("%d\n",ans);
    }
    return 0;
}