[CFGYM100221C]Forbidden Subwords

XII.[CFGYM100221C]Forbidden Subwords

”双向无限“之类东西太抽象了,我们不妨从简单点的地方考虑。比如说,有限串。

一个有限串中没有出现任何禁忌串的充要条件是什么?沿着AC自动机上边走时没有碰到任何结束点。

现在,我们考虑一个右侧无限的串(即有一个明确开头,但另一端没有结尾的串)。它不出现禁忌串的充要条件,依然是不经过结束点。

我们考虑将AC自动机中所有的结束点删去,仅保留非结束点。明显,剩下的图仍是一张有向图。

考虑对该有向图进行强连通分量染色。考虑染色后的一个SCC。

明显,其中必然存在至少一个简单有向环。

假如该有向环上还存在至少一条非环边的话,我们考虑一条单向无限的串,它从环上一点出发,并不断绕着它转圈。在某几圈里,我们绕着环走完整一圈;在某几圈里,我们直接走捷径从非环边直接插过去。明显,这样可以生成无限条合法单向无限串,于是答案就直接是 \(-1\)

否则,即所有强连通分量要么是简单环,要么是一个点。考虑缩点,生成一张DAG。我们把DAG上环缩成的点称作”环点“,其余点称作”单点“。

我们考虑若在DAG上,一个环点可以走到另一个环点,会发生什么:它可以在起始环点绕一圈,然后走到终止环点;也可以绕两圈,走到终止环点;三圈、四圈、五圈、更多圈……因此,我们发现,这样也会生成无限条合法串。

明显,除此之外,再无答案无限的情形。

现在我们考虑双向无限串。明显,若图中仍存在一非环SCC,则上述关于非环边的推断仍然成立,答案仍为无限。

否则,考虑一条双向无限串在DAG上的表现:其一定是在一个环点上绕无数圈,然后走一条路径到另一个环点,然后继续绕无数圈。

但是,若这条路径上还存在另一个环点,则在途径的这个环点,我们可以选择绕一圈、两圈、更多圈……因此答案为无穷。

也就是说,不能有一条路径经过三个及以上环点。这个很好判断。

否则,就直接求出从一个环点到另一个环点的路径数即可。这个随便拓扑一下就出来了。

时间复杂度 \(O\Big(|\Sigma|\sum|s|\Big)\),其中 \(|\Sigma|\) 是字符集大小。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int m,n,cnt=1,dfn[100100],low[100100],tot,col[100100],c,sz[100100];
struct AC_Automaton{
	int ch[6],fail;
	bool fob;
}t[100100];
char s[20];
void ins(){
	scanf("%s",s);int S=strlen(s);
	int x=1;
	for(int i=0;i<S;i++){
		if(!t[x].ch[s[i]-'a'])t[x].ch[s[i]-'a']=++cnt;
		x=t[x].ch[s[i]-'a'];
	}
	t[x].fob=true;
}
queue<int>q;
void build(){
	for(int i=0;i<m;i++){
		if(t[1].ch[i])t[t[1].ch[i]].fail=1,q.push(t[1].ch[i]);
		else t[1].ch[i]=1;
	}
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=0;i<m;i++){
			if(t[x].ch[i])t[t[x].ch[i]].fail=t[t[x].fail].ch[i],t[t[x].ch[i]].fob|=t[t[t[x].fail].ch[i]].fob,q.push(t[x].ch[i]);
			else t[x].ch[i]=t[t[x].fail].ch[i];
		}
	}
}
stack<int>stk;
void Tarjan(int x){
	dfn[x]=low[x]=++tot,stk.push(x);
	for(int i=0;i<m;i++){
		if(t[t[x].ch[i]].fob)continue;
		if(!low[t[x].ch[i]])Tarjan(t[x].ch[i]),low[x]=min(low[x],low[t[x].ch[i]]);
		else if(!col[t[x].ch[i]])low[x]=min(low[x],dfn[t[x].ch[i]]);
	}
	if(low[x]<dfn[x])return;
	c++;
	while(stk.top()!=x)col[stk.top()]=c,sz[c]++,stk.pop();
	col[stk.top()]=c,sz[c]++,stk.pop();
}
vector<int>v[100100];
bool cir[100100];
int dep[100100],in[100100];
ll f[100100],res;
int main(){
	freopen("forbidden.in","r",stdin);
	freopen("forbidden.out","w",stdout);
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;i++)ins();
	build();
	for(int i=1;i<=cnt;i++)if(!t[i].fob&&!dfn[i])Tarjan(i); 
	for(int i=1;i<=cnt;i++)for(int j=0;j<m;j++){
		if(t[i].fob||t[t[i].ch[j]].fob)continue;
		if(col[i]==col[t[i].ch[j]])sz[col[i]]--;
		else v[col[t[i].ch[j]]].push_back(col[i]);
	}
	for(int i=1;i<=c;i++){
		if(sz[i]<0){puts("-1");return 0;}
		cir[i]=!sz[i];
	}
	for(int i=1;i<=c;i++)for(auto j:v[i])in[j]++;
	for(int i=1;i<=c;i++)if(!in[i])q.push(i);
	while(!q.empty()){
		int x=q.front();q.pop();
		dep[x]+=cir[x],f[x]+=cir[x];
		if(cir[x])res+=f[x];
		if(dep[x]>=3){puts("-1");return 0;}
		for(auto y:v[x]){dep[y]=max(dep[y],dep[x]),f[y]+=f[x];if(!--in[y])q.push(y);}
	}
	printf("%lld\n",res);
	return 0;
}
原文地址:https://www.cnblogs.com/Troverld/p/14596734.html