Jzoj3172 贴瓷砖

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

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

今天翻看题库发现这道题忘记写题解了。。。

非常好的卡常题,题目也很裸,直接AC自动机+前缀和判定就好了,但是当时不知道为什么用了fenwick

#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 300010
#define M 4000100
using namespace std;
int n,m,ans=0; char s[300010],t[5010];
struct fenwick{
	int w[N],S;
	inline void add(int x,int v){ for(;x<=m;x+=x&-x) w[x]+=v; }
	inline int sum(int x){ for(S=0;x;x&=x-1) S+=w[x]; return S; }
}w;
int fail[M],son[M][26],c[M],cnt=1,v[26];
inline void insert(char* s){
	int x=1,l=0;
	for(;*s;++s,++l){
		v[*s-'a']=1;
		x=son[x][*s-'a']?son[x][*s-'a']:son[x][*s-'a']=++cnt;
	}
	c[x]=l;
}
inline void build(){
	queue<int> q;
	for(int i=0;i<26;++i)
		if(!son[1][i]) son[1][i]=1;
		else q.push(son[1][i]),fail[son[1][i]]=1;
	for(int x;!q.empty();q.pop()){
		x=q.front();
		for(int i=0;i<26;++i)
			if(!son[x][i]) son[x][i]=son[fail[x]][i];
		else q.push(son[x][i]),fail[son[x][i]]=son[fail[x]][i];
	}	
}
inline void query(char* s){
	for(int i=1,x=1;*s;++s,++i){
		x=son[x][*s-'a'];
		for(int j=x;j!=1;j=fail[j])
			if(c[j]){ w.add(i-c[j]+1,1); w.add(i+1,-1); }
	}
}
int main(){
	scanf("%d%s",&m,s);
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%s",t); insert(t);
	}
	for(int i=0;i<m;++i) v[s[i]-'a']=0;
	for(int i=0;i<26;++i) if(v[i]) goto end;
	build(); query(s); end:
	for(int i=1;i<=m;++i) if(w.sum(i)==0) ++ans;
	printf("%d
",ans);
}

原文地址:https://www.cnblogs.com/Extended-Ash/p/9477155.html