Description
DFA(确定性有限状态自动机)是一个有向图,顶点称为状态,边称为转移。每个转移用一个字母标记。对于每个状态s和每个转移l,至多有一个转移从s出发且标记为l。DFA有一个初始状态和若干个结束状态。DFA定义的语言的单词可以由从开始状态到结束状态的路径上所有的字母连接起来。
图中表示一个语言(包括单词:fix,foo,ox)的DFA。第一个DFA有7个状态,它不是最优的。第二个DFA定义的是同一个语言,但只有5个状态。
给定一个语言,包含有限个单词,求出定义该语言的DFA得最少状态数。
Input
第一行有一个整数n(1<=n<=5000),表示语言的单词数。接下来n行,每行一个单词。每个单词由1到30个小写字母组成。输入的所有单词互不相同。
Output
一个数,定义该语言的DFA得最少状态数。
等价的状态可以合并,两个状态等价当且仅当同时(不)为结束状态且出边及对应的状态相同
一开始建棵trie然后从叶节点开始将等价状态缩点
#include<cstdio> #include<algorithm> typedef unsigned long long u64; const int N=152000; int n,ans=0; int nx[N][26],id[N],pv[N],e[N],p=1; char s[64]; bool ed[N]; int q[N],qp=0,q1[N]; u64 h[N]; bool cmp(int a,int b){return h[a]<h[b];} u64 hash(int w){ u64 v=0; for(int i=0;i<26;i++)v=v*13999133+id[nx[w][i]]; if(e[w])v+=1844677; return v; } int main(){ scanf("%d",&n); while(n--){ scanf("%s",s); int w=1; for(int i=0;s[i];i++){ int c=s[i]-'a'; if(!nx[w][c])nx[w][c]=++p; pv[nx[w][c]]=w; w=nx[w][c]; } e[w]=1; } for(int i=1;i<=p;i++)id[i]=i; for(int i=1;i<=p;i++)if(e[i]){ bool is=1; for(int j=0;j<26;j++)if(nx[i][j]){is=0;break;} if(is)ed[q[qp++]=i]=1; } while(qp){ int m=qp; qp=0; for(int i=0;i<m;i++)h[q1[i]=q[i]]=hash(q[i]); std::sort(q1,q1+m,cmp); for(int i=0,j=0;i<m;i=j){ while(h[q1[i]]==h[q1[j]])id[q1[j++]]=q1[i]; if(i+1<j)for(int k=i;k<j;k++)if(!ed[pv[q1[k]]])ed[q[qp++]=pv[q1[k]]]=1; } } for(int i=1;i<=p;i++)if(id[i]==i)++ans; printf("%d",ans); return 0; }