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