[JZOJ3427]【NOIP2013模拟】归途与征程

Description

这里写图片描述

Solution

注意,这里的匹配是全部都要匹配,不能落下一个字符。

A 串中,”* “将字符串分成若干个区间(不包括”*”)。
bz[i][j]表示 A 串中第i个区间和 B 串中第j位开头能否匹配
对于每一个区间,都与 B 串做一次KMP,可以在 O(NM) 的时间范围内求出 bz

然后我们只需要把 B 串复制一遍,再枚举开头判断就好。

但是这样判断的复杂度达到O(M),总的是 O(M2)

考虑优化判断。

于是可以预处理 next[i][j] 表示 bz[i+1][k]=1 大于 j k最小值

这样复杂度就变成 O(NM)

Code

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
int n,m,m1,num,a[101],next[101][200001];
char s1[101],s2[200001];
bool bz[101][200001];
void kmp(int ar,int lt,int rt)
{
    if (rt<lt)
    {
        int i;
        fo(i,0,m1+1) bz[ar][i]=1;
        return; 
    }
    int len=rt-lt+1,i,i1,j=0,j1=0,p[101];
    p[1]=0;
    fo(i1,2,len)
    {
        i=i1+lt-1;
        j1=j+lt-1;
        while (j>0&&s1[j1+1]!=s1[i]) 
        {
            j=p[j];
            j1=j+lt-1;
        }
        if (s1[j1+1]==s1[i]) j++;
        j1=j+lt-1;
        p[i1]=j;
    }
    j=0;
    fo(i,1,m1)
    {
        j1=j+lt-1;
        while (j>0&&s1[j1+1]!=s2[i]) 
        {
            j=p[j];
            j1=j+lt-1;
        }
        if (s1[j1+1]==s2[i]) j++;
        j1=j+lt-1;
        if (j==len) bz[ar][i-j+1]=1;
    }
}
bool pd(int st)
{
    int i=st,j=1;
    if (bz[j][st]==0) return 0; 
    a[0]=0;
    while (i!=0&&i+a[j]-a[j-1]-2<=st+m-1) 
    {
        if (j==num-1&&bz[num][st+m-a[num]+a[num-1]+1]) return 1;
        i=next[j][i+a[j]-a[j-1]-2];
        j++;
    }
    return 0; 
}
int main()
{
    scanf("%s",s1+1);
    scanf("\n");
    scanf("%s",s2+1);
    n=strlen(s1+1);
    m=strlen(s2+1);
    num=0;
    int i,j;
    fo(i,1,m-1) s2[i+m]=s2[i]; 
    m1=2*m-1;
    fo(i,1,n) if (s1[i]=='*') a[++num]=i;
    a[++num]=n+1;
    memset(bz,0,sizeof(bz));
    fo(i,1,num)
    {
        kmp(i,a[i-1]+1,a[i]-1); 
    } 
    fod(i,m1-1,0)
    {
        fo(j,0,num-1)
        {
            next[j][i]=bz[j+1][i+1]?i+1:next[j][i+1];
        }
    }
    int ans=0;
    fo(i,1,m) 
    {
        if (i==2)
        {
            n++;
            n--;
        }
        if(pd(i)) ans++;
    }
    cout<<ans;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值