BZOJ3413: 匹配

题面:https://www.lydsy.com/JudgeOnline/problem.php?id=3413
题解:
首先考虑匹配次数的意义。可以看出匹配的过程就是拿\(A\)串的所有前缀
\(B\)串一一匹配。考虑\(A\)串的每一位。设当前位一共被匹配了\(f[i]\)次,
那么答案即为\(\sum\) \(f[i]\)
考虑\(f[i]\)的组成。它包括了两个部分:匹配成功的次数和失配次数。
其中,失配次数是可以单独计算的。
我们可以给\(A\)串建立SAM,用\(B\)串在上面跑。
如果跑到了0号节点,说明整个匹配过程是失败的,所以失配次数为\(n\)
否则,失配次数就是跑到的那个节点的endpos最靠左的那个节点。
至于如何记这个endpos,可以用线段树合并。具体实现就是先对所有SAM上
的节点拓扑排序,然后从大到小合并上去即可。
现在考虑匹配成功的次数。考虑\(B\)串在\(A\)串的SAM上跑的过程。
每次跑到一个非根节点,这个节点的线段树上存有当前位置所有的endpos。
设失配次数为\(x\),那么当前节点能计入贡献的只有endpos<=\(x\)的那些
(注意这里的endpos代表的是一个前缀的一个子串),其他的那些因为已经匹配完了,
所以不计入贡献。
时间复杂度:\(O(SlogS)\)\(S\)指的是\(B\)串总长)
代码:

#include
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
templateI read(D &res){
    res=0;register D g=1;register char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')g=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        res=(res<<3)+(res<<1)+(ch^48);
        ch=getchar();
    }
    res*=g;
}
const int INF=1e9+7;
char c[101000];
int n,m,leng,sum,root[202000],lc[4040000],rc[4040000],w[4040000];
I modi(int &k,int l,int r,int x){
    if(!k)k=++sum;w[k]++;
    if(l==r)return;
    re mid=(l+r)>>1;
    if(x<=mid)modi(lc[k],l,mid,x);
    else modi(rc[k],mid+1,r,x);
}
IN merge(int x,int y){
    if(!x&&!y)return 0;
    if(!x)return y;
    if(!y)return x;
    re now=++sum;
    lc[now]=merge(lc[x],lc[y]);rc[now]=merge(rc[x],rc[y]);w[now]=w[x]+w[y];
    return now;
}
IN ques_min(int k,int l,int r){
    if(l==r)return l;
    re mid=(l+r)>>1;
    if(w[lc[k]])return ques_min(lc[k],l,mid);
    else return ques_min(rc[k],mid+1,r);
}
inline ll ques_sum(int k,int l,int r,int lim){
    if(!k||l>lim)return 0;
    if(l==r||r<=lim)return w[k];
    re mid=(l+r)>>1;
    return ques_sum(lc[k],l,mid,lim)+ques_sum(rc[k],mid+1,r,lim);
}
namespace SAM{
    ll ans;
    int len[202000],link[202000],ch[202000][10],buc[202000],sa[202000],p,q,las,cur,tot,cle;
    I add(int x){
        len[cur=++tot]=len[las]+1,p=las,las=cur;
        while(p&&!ch[p][x])ch[p][x]=cur,p=link[p];
        if(!p){link[cur]=1;return;}
        q=ch[p][x];
        if(len[p]+1==len[q]){link[cur]=q;return;}
        cle=++tot;len[cle]=len[p]+1,link[cle]=link[q];
        memcpy(ch[cle],ch[q],sizeof(ch[q]));
        while(p&&ch[p][x]==q)ch[p][x]=cle,p=link[p];
        link[q]=link[cur]=cle;
    }
    I init(){
        tot=las=1;
        F(i,1,n)add(c[i]-'0'),modi(root[cur],1,n,i);
        F(i,1,tot)buc[len[i]]++;
        F(i,1,tot)buc[i]+=buc[i-1];
        FOR(i,tot,1)sa[buc[len[i]]--]=i;
        FOR(i,tot,1)if(sa[i]!=1)root[link[sa[i]]]=merge(root[link[sa[i]]],root[sa[i]]);
    }
    I ques(){
        p=1;q=INF;
        F(i,1,leng)p=ch[p][c[i]-'0'];
        if(!p)ans=n;
        else{
            q=ques_min(root[p],1,n);
            ans=q-leng;
        }
        p=1;
        F(i,1,leng){
            p=ch[p][c[i]-'0'];
            if(!p)break;
            ans+=ques_sum(root[p],1,n,q+i-leng);
        }
        printf("%lld\n",ans);
    }
}
int main(){
    read(n);
    scanf("%s",c+1);
    SAM::init();
    read(m);
    while(m--){
        scanf("%s",c+1);
        leng=strlen(c+1);
        SAM::ques();
    }
    return 0;
}

你可能感兴趣的:(BZOJ3413: 匹配)