求dfa最小化状态
想fhq的小强的颜色都还没做出来
一颗tire是一个满足要求的dfa,但是并不是最小化,我们需要在trie的基础上修改,是他保持原来性质,但是状态量更小
如果两个状态的子树同构的话而且同为或同不为结束状态,那么这两个状态是可以合并的,对于子树可以用以前集训队论文中介绍过的hash来做
考虑两个状态的结合会不会影响其他状态,显然子状态是没有影响的,因为子树是否合并是看子树本身性质而不是祖先性质,而对于祖先来说,子树合并后,自己的子树依旧是相同的,否则就不满足与原trie同构的性质,所以一遍预处理出hash值后以后可以沿用。
#include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> const int lucknum = 1995309; using namespace std; long long c[500000],hash[500000],d[500000]; char ch[1000]; int n,next[200000][27],ans,ss,s1,st[200000],b[200000],u[1000000]; bool final[1000000]; void ins(char ch[1000]) { int len,s,x,i; len=strlen(ch+1); for (s=0,i=1;i<=len;i++) { x=ch[i]-'a'; if (!next[s][x]) next[s][x]=++ss; s=next[s][x]; } final[s]=1; } bool cmp(int i,int j) { if (d[i]!=d[j]) return d[i]<d[j]; if (hash[i]!=hash[j]) return hash[i]<hash[j]; return final[i]<final[j]; } void bfs(int s) { int h,r,i,ne,na,j; h=r=0; st[++r]=s,d[s]=0; for (;h<r;) { ne=st[++h]; for (i='a'-'a';i<='z'-'a';i++) if (next[ne][i]) { na=next[ne][i]; d[na]=d[ne]+1; st[++r]=na; } } for (i=r;i>=2;i--) { ne=st[i],d[ne]=1; hash[ne]=1; for (j='a'-'a';j<='z'-'a';j++) { na=next[ne][j]; if (!na) continue; hash[ne]+=(hash[na]+(final[na] ? lucknum : 0))*c[j]; if (d[na]+1>d[ne]) d[ne]=d[na]+1; } } for (i=0;i<=ss;i++) u[i]=i; sort(u,u+ss+1,cmp); for (i=0;i<=ss;i++) if (d[u[i]]==d[u[i-1]] && final[u[i]]==final[u[i-1]] && hash[u[i]]==hash[u[i-1]]) b[u[i]]=u[i-1]; } void init() { int i; scanf("%d\n",&n); ss=0; for (i=1;i<=n;i++) { scanf("%s\n",ch+1); ins(ch); } for (i=0;i<=ss;i++) b[i]=i; for (c[0]=1,i=1;i<='z';i++) c[i]=(c[i-1]*13); bfs(0); ans=0; for (i=0;i<=ss;i++) ans+=(b[i]==i); printf("%d\n",ans); } int main() { init(); return 0; }