hdu 3259 Just Another String Matching Problem

所有的匹配子串必然是由表达式中的正常字符串连接起来的,而连接的符号串很容易表达:当只存在 ? 时,两个子串的距离等于?的个数;反之,两个子串的距离大于等于 ? 的个数。
那么考虑按表达式中的正常字符串来进行dp,简单推出一个方程:f(i,j)表示第 i 个正常字符串以第j个出现位置为结尾的合法子串的个数,那么按照连接的符号串可以有转移 f(i,j)=f(i1,k)|posk=posjlength(i)|?| f(i,j)=f(i1,k)|poskposjlength(i)|?| ,其中 length 表示该串的长度, pos 表示串出现位置的末尾。
然而上述方程是错误的,因为当同一个串连接了之前的合法串时,会导致第一个位置相同的串被重复计数。考虑直接用bitset记录每个串的第一个位置,那么当连接串包含 时,方程变为f(i,j)=orf(i1,k)|poskposjlength(i)|?|
显然状态数最多的时候不会超过 n2 个,单层状态数不会超过 n 个,转移的复杂度是O(n2logn+n264)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<bitset>
#include<vector>
#include<queue>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
typedef long long ll;
typedef bitset<3000> bt;
int nx[3010][26],gfa[3010],tn,n,m,sn,xd[3010],sle[3010],ct=0;bool fd[3010];
char cz[3010],xz[3010];
bt dp[2][3010];
vector<int>vc[3010],acd[3010];
void _psh(int l,int r,int si){
    int t,d;for(sle[si]=r-l+1,d=0;l<=r;++l){
        t=xz[l]-'a';
        if(nx[d][t])d=nx[d][t];
        else{
            nx[d][t]=++tn;d=tn,clr(nx[tn]);
        }
    }
    acd[d].push_back(si);
};
void _clgfa(){
    int i,t,d;queue<int>qe;
    for(i=0;i<26;++i)if(nx[0][i])
        gfa[nx[0][i]]=0,qe.push(nx[0][i]);
    for(;!qe.empty();){
        t=qe.front();qe.pop();
        for(i=0;i<26;++i)if(nx[t][i]){
            for(d=gfa[t];d&&!nx[d][i];d=gfa[d]);
            gfa[nx[t][i]]=nx[d][i],qe.push(nx[t][i]);
        }
    }
};
void __cl(){
    int i,j,d,t,x;for(i=d=0;i<n;++i){
        t=cz[i]-'a';
        if(nx[d][t])d=nx[d][t];
        else{
            for(x=gfa[d];x&&!nx[x][t];x=gfa[x]);
            d=nx[x][t];
        }
        for(x=d;x;x=gfa[x])for(j=0;j<acd[x].size();++j)
            vc[acd[x][j]].push_back(i);
    }
};
int cu,qi;
int _bf_eq(int si,int x){
    int b=0,e=vc[si].size()-1,m;
    while(b<=e){
        m=(b+e)>>1;
        if(vc[si][m]==x)return m;
        else if(vc[si][m]>x)e=m-1;
        else
            b=m+1;
    }
    return -1;
};
int _bf_le(int si,int x){
    int b=0,e=vc[si].size()-1,m,t=-1;
    while(b<=e){
        m=(b+e)>>1;
        if(vc[si][m]<=x)t=m,b=m+1;
        else
            e=m-1;
    }
    return t;
};
void _cl(int si){
    int i,j,t;
    if(!si){
        cu=0,qi=1;
        if(fd[si]){
            for(clr(dp),i=0;i<vc[si].size();++i){
                if(vc[si][i]-sle[si]-xd[si]+1>=0)
                    dp[cu][i][vc[si][i]-sle[si]-xd[si]+1]=1;
            }
        }
        else{
            for(clr(dp),i=0;i<vc[si].size();++i){
                for(j=0;vc[si][i]-sle[si]-xd[si]+1>=j;++j)
                    dp[cu][i][j]=1;
            }
        }
    }
    else{
        swap(cu,qi);
        if(fd[si]){
            for(i=0;i<vc[si].size();++i){
                t=_bf_eq(si-1,vc[si][i]-sle[si]-xd[si]);
                dp[cu][i]=t>=0?dp[qi][t]:0;
            }
        }
        else{
            for(i=1;i<vc[si-1].size();++i)dp[qi][i]|=dp[qi][i-1];
            for(i=0;i<vc[si].size();++i){
                t=_bf_le(si-1,vc[si][i]-sle[si]-xd[si]);
                dp[cu][i]=t>=0?dp[qi][t]:0;
            }
        }
    }
};
void cl(){
    int i,j,k,d;scanf("%s",cz);scanf("%s",xz);
    n=strlen(cz),m=strlen(xz);
    for(i=0;i<=3000;++i)acd[i].clear(),vc[i].clear();
    for(i=tn=sn=0,clr(nx[0]);i<m;){
        xd[sn]=0,fd[sn]=1;for(;i<m&&xz[i]<'a';++i){
            if(xz[i]=='*')fd[sn]=0;
            if(xz[i]=='?')xd[sn]++;
        }
        if(i==m)break;
        for(j=i;i<m&&xz[i]>='a';++i);_psh(j,i-1,sn++);
    }
    if(!sn){
        if(fd[0])printf("Case %d: %d\n",++ct,max(0,n-xd[0]+1));
        else{
            for(j=0,k=xd[0]?xd[0]:1;k<=n;++k)j+=(n-k+1);
            printf("Case %d: %d\n",++ct,j);
        }
        return;
    }
    _clgfa();__cl();
    for(i=0;i<sn;++i)if(vc[i].empty()){
        printf("Case %d: 0\n",++ct);return;
    }
    for(i=0;i<sn;_cl(i++));
    for(xd[sn]=0,fd[sn]=1,i=m-1;i>=0&&xz[i]<'a';--i){
        if(xz[i]=='*')fd[sn]=0;
        if(xz[i]=='?')xd[sn]++;
    }
    if(fd[sn]){
        for(k=i=0;i<vc[sn-1].size()&&vc[sn-1][i]<=n-1-xd[sn];++i)k+=dp[cu][i].count();
        printf("Case %d: %d\n",++ct,k);
    }
    else{
        for(i=1;i<vc[sn-1].size();++i)dp[cu][i]|=dp[cu][i-1];
        for(k=i=0;i<n;++i){
            d=_bf_le(sn-1,i-xd[sn]);
            k+=(d>=0?dp[cu][d].count():0);
        }
        printf("Case %d: %d\n",++ct,k);
    }
};
int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    int t;scanf("%d",&t);while(t--)cl();
    return 0;
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值