POJ 3576 Language Recognition

题目大意:

给N个(N不超过5000)长度不超过30的仅有小写字母组成的字符串, 构造状态数最小的DFA.

 

简要分析:

有限自动机状态最小化似乎是有论文介绍高级方法的...我们学OI就不用那么专业了. 关于DFA的解释参考http://en.wikipedia.org/wiki/Deterministic_finite_automaton.

首先Trie树肯定是DFA, 所以我们先把Trie建出来. 因为要保证简化后的DFA与Trie这个DFA同构, 而同构的含义是能识别的语言完全相同, 因为字符串没有无限长的, 所以不会有环, 即最终的DFA必定是DAG. 现在我们考虑合并Trie中的结点. 按照每个状态往下走到最深处的距离为该点的深度, 按深度分组, 则能两个状态合并的充分条件是两个状态在同一组. 于是我们按深度从小到大处理每一组, 把相同的状态缩到一起. 所谓相同, 有三方面: 是否同为final态或同不为, 转移方式完全相同, 转移到的状态完全相同. 这个可以通过哈希来判断. 于是时间复杂度大概是是O(SlogS), S为Trie中的状态数.

 

代码实现:

View Code
  1 #include <cstdio>
2 #include <cstdlib>
3 #include <cstring>
4 #include <algorithm>
5 using namespace std;
6
7 typedef unsigned long long hash_t;
8 const int MAX_N = 5000, MAX_L = 30, SON = 26, MAX_NODE_CNT = MAX_N * MAX_L + 1, SEED = 99991;
9 char pat[MAX_L + 1];
10 int n, dep[MAX_NODE_CNT], seq[MAX_NODE_CNT];
11
12 struct disjoint_set_t {
13 int root[MAX_NODE_CNT];
14 int n;
15 void set(int _n) {
16 n = _n;
17 for (int i = 0; i < n; i ++) root[i] = i;
18 }
19 int get_root(int x) {
20 return x == root[x] ? x : root[x] = get_root(root[x]);
21 }
22 void set_union(int u, int v) {
23 u = get_root(u);
24 v = get_root(v);
25 root[u] = v;
26 }
27 } ds;
28
29 struct node_t {
30 int son[SON], sc;
31 hash_t hash;
32 bool end;
33 } node[MAX_NODE_CNT];
34
35 int node_idx, root;
36
37 int node_alloc() {
38 int ret = node_idx ++;
39 memset(node[ret].son, -1, sizeof(int) * SON);
40 node[ret].sc = 0;
41 node[ret].end = 0;
42 return ret;
43 }
44
45 void init() {
46 node_idx = 0;
47 root = node_alloc();
48 }
49
50 void ins(char *str) {
51 int pos = root;
52 while (*str) {
53 int t = *(str ++) - 'a';
54 if (node[pos].son[t] < 0) {
55 node[pos].son[t] = node_alloc();
56 node[pos].sc ++;
57 }
58 pos = node[pos].son[t];
59 }
60 node[pos].end = 1;
61 }
62
63 void dfs(int u) {
64 dep[u] = 0;
65 for (int i = 0; i < SON; i ++)
66 if (node[u].son[i] >= 0) {
67 dfs(node[u].son[i]);
68 dep[u] = max(dep[u], dep[node[u].son[i]]);
69 }
70 dep[u] ++;
71 }
72
73 bool cmp(const int &a, const int &b) {
74 return dep[a] < dep[b];
75 }
76
77 bool cmp_s(const int &a, const int &b) {
78 if (node[a].sc != node[b].sc) return node[a].sc < node[b].sc;
79 return node[a].hash < node[b].hash;
80 }
81
82 int main() {
83 while (scanf("%d", &n) != EOF && n) {
84 init();
85 for (int i = 0; i < n; i ++) {
86 scanf("%s", pat);
87 ins(pat);
88 }
89
90 dfs(root);
91 for (int i = 0; i < node_idx; i ++) seq[i] = i;
92 sort(seq, seq + node_idx, cmp);
93 int ans = node_idx;
94 ds.set(node_idx);
95
96 for (int i = 0, j = 0; i < node_idx; i = j) {
97 while (j < node_idx && dep[seq[j]] == dep[seq[i]]) j ++;
98 for (int k = i; k < j; k ++) {
99 node[seq[k]].hash = 0ULL;
100 for (int s = 0; s < SON; s ++) {
101 int ss = node[seq[k]].son[s] >= 0 ? ds.get_root(node[seq[k]].son[s]) + 2 : 1;
102 node[seq[k]].hash = node[seq[k]].hash * SEED + ss;
103 }
104 node[seq[k]].hash = node[seq[k]].hash * SEED + node[seq[k]].end + 1;
105 }
106 sort(seq + i, seq + j, cmp_s);
107 for (int k = i, l = i; k < j; k = l) {
108 while (l < j && node[seq[k]].sc == node[seq[l]].sc && node[seq[k]].hash == node[seq[l]].hash) l ++;
109 ans -= (l - k - 1);
110 for (int t = k + 1; t < l; t ++) ds.set_union(seq[k], seq[t]);
111 }
112 }
113
114 printf("%d\n", ans);
115 }
116 return 0;
117 }
posted @ 2012-03-10 21:53  zcwwzdjn  阅读(773)  评论(0编辑  收藏  举报