【JZOJ 3427】归途与征程

Description

这里写图片描述

Solution

题意简化以后就是:在b串的同构中,有多少个可以按顺序匹配多个串。
我们可以先把a串拆开,每个做一遍KMP的预处理,
倍长b串,枚举b串的开头,依次匹配过去,
每次匹配的时候,记录一下上一次匹配失败的位置,直接从上次匹配失败的地方开始匹配;成功是也记录一下,判断一下当前可不可以用那个匹配成功的去匹配当前的位置。
复杂的: O(nm) ;

Code

#include<cstdio>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=100500;
int m,n,ans;
char b[N*2],a[N];
int nx[N],A[N][2],A0;
int B[N],L[N];
int max(int a,int b){return a<b?b:a;}
bool ss(int q,int l,int r)
{
    l=max(l,L[q]);
    if(A0==1&&l!=r+1)return 0;
    if(q>A0 && l<=r+1)return 1;
    if(r-l<A[q][1]-A[q][0])return 0;
    if(q==A0 && a[n]!='*')
    {
        int w=r-(A[q][1]-A[q][0])-A[q][0];
        fo(i,A[q][0],A[q][1])if(a[i]!=b[w+i])return 0;
        return 1;
    }
    int j=A[q][0]-1;
    if(B[q]>=l)return ss(q+1,B[q]+A[q][1]-A[q][0],r);
    fo(i,l,r)
    {
        while(j>=A[q][0]&&a[j+1]!=b[i])j=nx[j];
        if(a[j+1]==b[i])j++;
        if(j>=A[q][1])
        {
            bool z=ss(q+1,i+1,r);
            B[q]=z*(i-j+2);
            return z;
        }
    }
    L[q]=max(L[q],r-j);
    return 0;
}
int main()
{
    int q,w;char ch;
    for(ch=getchar();ch<='z'&&ch>='a'||ch=='*';ch=getchar())a[++n]=ch;
    a[n+1]=a[0]='*';
    for(;!(ch<='z'&&ch>='a');ch=getchar());
    for(;ch<='z'&&ch>='a';ch=getchar())b[++m]=ch;
    fo(i,1,m)b[m+i]=b[i];
    fo(i,1,n)if(a[i]!='*')
    {
        if(a[i-1]=='*')A[++A0][0]=i;
        A[A0][1]=i;
    }
    fo(I,1,A0)
    {
        int j=A[I][0]-1;
        nx[A[I][0]]=j;
        fo(i,A[I][0]+1,A[I][1])
        {
            while(j>=A[I][0]&&a[j+1]!=a[i])j=nx[j];
            if(a[j+1]==a[i])j++;
            nx[i]=j;
        }
    }
    ans=0;
    if(!A0)ans=m;else
    for(int i=1,j=0;i-j<=m;i++)
    if(a[1]=='*')ans+=ss(1,i,i+m-1);else
    {
        while(j>=A[1][0]&&a[j+1]!=b[i])j=nx[j];
        if(a[j+1]==b[i])j++;
        if(j>=A[1][1])
        {
            ans+=ss(2,i+1,i-j+m);
            j=nx[j];
        }
    }
    printf("%d\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值