[2012-2013ACM-ICPC,NEERC,Western Subregional Contest]Pattern Matching/[JZOJ3427]归途与征程

题目大意

S 是一个非空字符串,包含小写拉丁字母以及特殊字符 T 是另一个非空字符串,只包含小写拉丁字母。
定义S能匹配 T 当且仅当:除了S中的 能匹配T中任意多个字符(包括空串)以外,其余字符一一对应相同。
T 的所有循环同构串(包括自己)中能与S匹配的串的个数。
循环同构串:将原串某前缀一道后面形成的串。在这里即使循环同构串看起来相同,但只要所选前缀不一样,就算做多个(即 T |T|个循环同构串)。

1|T|100,1|S|100000


题目分析

我们将 T 串倍长以方便处理循环同构。
S根据 分成若干块,令fi,j表示 T 中第i位开始第一个能匹配 S 的第j块的位,这个可以暴力计算。
然后我们利用这个就可以模拟匹配过程了,注意开头结尾如果不是 需要特殊处理。
还有就是细节很多,请读者自行思考。
时间复杂度O(|S||T|)
Codeforces上空间卡的紧,不能倍长 T 串,其实我们只用加长S位即可。


代码实现

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>

using namespace std;

const int M=100500;
const int N=105;

int part[N],len[N];
int n,m,ans,p,l;
char A[N],B[M];
int f[M][N];

bool match(int st,int en)
{
    int cur=1,fn=p;
    if (A[0]!='*'&&f[st][1]!=st) return false;
    if (A[0]!='*') st+=len[1],cur++;
    if (A[n-1]!='*'&&(en-len[p]+1<0||f[en-len[p]+1][p]!=en-len[p]+1)) return false;
    if (A[n-1]!='*') en-=len[p],fn--;
    if (st>en) return true;
    for (int nxt;cur<=fn;st=nxt+len[cur],cur++)
    {
        nxt=f[st][cur];
        if (nxt+len[cur]-1>en) return false;
    }
    return true;
}

int main()
{
    freopen("journey.in","r",stdin),freopen("journey.out","w",stdout);
    scanf("%s",A),n=strlen(A);
    scanf("%s",B),l=m=strlen(B);
    for (int i=m;i<m+n;i++) B[i]=B[i-m];
    if (A[0]=='*')
    {
        if (A[1]!='*') part[p=1]=1;
    }
    else part[p=1]=0;
    for (int i=1;i<n;i++)
        if (A[i]=='*'&&i<n-1&&A[i+1]!='*')
            part[++p]=i+1;
    for (int i=1;i<=p;i++)
        for (int j=part[i];j<n&&A[j]!='*';j++)
            len[i]=j-part[i]+1;
    m=m+n,ans=0,part[p+1]=n+1;
    if (A[n-1]=='*') part[p+1]=n;
    for (int i=1;i<=p;i++) f[m][i]=m;
    for (int i=m-1;i>=0;i--)
    {
        for (int j=1;j<=p;j++)
        {
            bool AC=true;
            for (int st=part[j];st<=part[j]+len[j]-1;st++)
                if (A[st]!=B[i+st-part[j]])
                {
                    AC=false;
                    break;
                }
            f[i][j]=AC?i:f[i+1][j];
        }
    }
    for (int i=0;i<l;i++)
        ans+=match(i,i+l-1);
    printf("%d\n",ans);
    fclose(stdin),fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值