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;
}