您的位置:首页 > 运维架构

【COCI2012 Task 5】T6 poplocavanje ([JZOJ3172]贴瓷砖)(AC自动机模板)

2016-07-16 16:31 351 查看

Description

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

问街道有多少字母(地方)不能被瓷砖覆盖。

Solution

似乎有的地方翻译成广告牌~

显然跑AC自动机,求出每个位置向前最多匹配多少位,扫一遍就好。

注意卡空间

还有一个优化,设up[i]表示沿着fail链最近的有值的节点(就是是单词末尾的节点),预处理出来,避免沿着fail向上跳查漏时复杂度变成O(NL)

Code

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define MAXN 300005
using namespace std;
int num(char ch)
{
int i=ch;
return i-95;
}
int trie[4000000][26],n,m,nd,len,fail[14*MAXN],ft[14*MAXN],ct[14*MAXN],key[14*MAXN],dt[14*MAXN],dep[14*MAXN],sm[MAXN],up[14*MAXN];
char s[MAXN],word[5005];
void build(int k,int now)
{
int cn=word[k]-97;
if(k>len)
{
ct[now]++;
return;
}
if (trie[now][cn]==0) trie[now][cn]=++nd;
ft[trie[now][cn]]=now;
key[trie[now][cn]]=cn;
build(k+1,trie[now][cn]);
}
void make()
{
dt[1]=1;
int i=0,j=1,k,now;
while (i<j)
{
now=dt[++i];
int temp=fail[ft[now]];
while (trie[temp][key[now]]==0) temp=fail[temp];
if (now!=1) fail[now]=trie[temp][key[now]];
if (now==1||ct[now]) up[now]=now;
else up[now]=up[fail[now]];
fo(k,0,25)
if (trie[now][k]!=0)
{
dt[++j]=trie[now][k];
dep[dt[j]]=dep[now]+1;
}
}
}
void match()
{
int p=1,k=0;
while(k<n)
{
bool bz=0;
int cn=s[++k]-'a';
while (trie[p][cn]==0) p=fail[p];
p=trie[p][cn];
sm[k]=dep[up[p]];
/*
int temp=p;
while (temp!=1)
{
if (ct[temp]) sm[k]=max(sm[k],dep[temp]);
temp=fail[temp];
}(正常的AC自动机应该是这样的)
*/
}
}
int main()
{
cin>>n;
scanf("\n");
scanf("%s",s+1);
cin>>m;
memset(ct,0,sizeof(ct));
nd=1;
int i;
fo(i,1,m)
{
scanf("\n");
scanf("%s",word+1);
len=strlen(word+1);
build(1,1);
}
fail[1]=0;
ft[1]=0;
dep[1]=0;
fo(i,0,25) trie[0][i]=1;
make();
match();
int ans=0;
i=n;
int j=0;
while (i>0)
{
j=max(j,sm[i]);
if (j==0) ans++;
else j--;
i--;
}
cout<<ans;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: