JZOJ 3172.【GDOI2013模拟4】贴瓷砖

题目

A镇的主街是由N个小写字母构成(S串),镇长准备在上面贴瓷砖,瓷砖一共有M种,第i种上面有Li个小写字母,瓷砖不能旋转也不能被分割开来,瓷砖只能贴在跟它身上的字母完全一样的地方,允许瓷砖重叠,并且同一种瓷砖的数量是无穷的。
问街道有多少字母(地方)不能被瓷砖覆盖。

题解

有卡住人的地方。
①匹配的时候还没走到底端但已经确认能够匹配了。
②建造AC自动机是以谁来建造,怎么跑?
很容易想到的是,按照瓷砖来构建AC自动机。然后用S串跑一遍。
这个怎么办?
设up[i]表示i的fail链上第一个“终点”。
然后构造fail链的时候同时处理up[]。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define N 4000010
#define M 300010
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int i,j,k,l,n,m,x,y,z,tot,ans;
int fail[N],up[N],c[N],tr[N][26],dep[N];
int sum[M];
char s[N],s1[5010],ch;
queue<int>qu;
int main(){
    scanf("%d\n",&n);
    fo(i,1,n)s[i]=getchar();
    scanf("%d\n",&m);
    dep[0]=0;
    while(m--){
        l=0;
        ch=getchar();
        while(ch<'a'||ch>'z')ch=getchar(); 
        while(ch>='a'&&ch<='z')s1[++l]=ch,ch=getchar();
        k=0;
        fo(i,1,l){
            x=s1[i]-'a';
            if(tr[k][x])k=tr[k][x];
            else{
                tr[k][x]=++tot;
                dep[tot]=dep[k]+1;
                k=tot;
            }
            if(i==l)c[k]=1;
        }
    }
    fail[0]=-1;
    qu.push(0);
    while(!qu.empty()){
        x=qu.front();
        qu.pop();
        fo(i,0,25){
            if(!tr[x][i])continue;
            y=tr[x][i];z=fail[x];
            while(~z&&!tr[z][i])z=fail[z];
            fail[y]=z==-1?0:tr[z][i];
            qu.push(y);
            up[y]=y;
            if(!c[y])up[y]=up[fail[y]];
        }
    }
    j=0;
    fo(i,1,n){
        x=s[i]-'a';
        while(~j&&!tr[j][x])j=fail[j];
        j=j==-1?0:tr[j][x];
        sum[i-dep[up[j]]+1]++;
        sum[i+1]--;
    }
    k=0;ans=n;
    fo(i,1,n){k+=sum[i];if(k)ans--;}
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值