hopcroft法的复杂度,他们说是nlogn,可是都没有严格的证明。难得找到一篇讲的详细点的论文,却又啰里啰唆的,不过那篇论文里面采用的是颜色树这个结构,有点意思。
前面的那个算法是n的平方复杂度,虽然这个复杂度计算都是建立在一些操作为单位时间操作的基础上。可是这些被认为的单位时间操作在我的实现中却有着平方复杂度,情何以堪,万恶的理论计算机科学家。
hopcroft实现的代码,太长了,还没写完。不过核心的子集分割已经完成了,剩下的就是分配节点及重新构建邻接表。明天再说吧。
1 #include "dfa_to_dfa.h" 2 pdfa_edge* rev_dfa_table;//作为dfa的反转表 3 int* belong_to;//这个是用来标记每个节点所在的群的标号 4 int** group_table;//这个是用来串联所有的群 5 int group_index;//这个用来表明使用了多少号的群 6 typedef struct _group_list 7 //这个结构用来把所有的当前在使用的群的标号串联起来 8 //这样就可以用来替代掉栈,而且也有利于遍历 9 { 10 struct _group_list* next; 11 int group_number; 12 }group_list,*pgroup_list; 13 pgroup_list group_list_begin=NULL;//这个是链表的专用头节点 14 void insert_group(int in_group_number)//将一个新的群号加入到群列表中,这里默认已经经过参数检查了 15 { 16 pgroup_list temp_list; 17 temp_list=malloc(sizeof(struct _group_list)); 18 temp_list->group_number=in_group_number; 19 temp_list->next=group_list_begin->next; 20 group_list_begin->next=temp_list; 21 } 22 23 24 void reverse_dfa(void)//构建反转表 25 { 26 int for_travel;//遍历变量 27 pdfa_edge temp_add;//作为反转表的增加边的变量 28 pdfa_edge temp_travel;//用来遍历原来的dfa表的邻接表的变量 29 int temp_dest;//增加边用的临时起始编号 30 rev_dfa_table=malloc(sizeof(struct _dfa_edge)*(dfa_node_number+1));//分配表的内存,注意加1 31 for(for_travel=1;for_travel<=dfa_node_number;for_travel++) 32 { 33 rev_dfa_table[for_travel]=NULL;//初始化为空 34 } 35 for(for_travel=1;for_travel<=dfa_node_number;for_travel++) 36 { 37 temp_travel=current_dfa_table[for_travel].begin; 38 while(temp_travel!=NULL) 39 { 40 temp_add=malloc(sizeof(struct _dfa_edge)); 41 temp_add->destination_number=for_travel; 42 temp_add->label=temp_travel->label; 43 temp_dest=temp_travel->destination_number; 44 temp_add->next=rev_dfa_table[temp_dest]; 45 rev_dfa_table[temp_dest]=temp_add; 46 } 47 }//现在已经完全构建好了反转表 48 } 49 50 51 52 typedef struct _rev_hash 53 { 54 int in_use;//0表示未使用,1表示正在使用,2表示已经删除 55 char* name; 56 int dfa_group_index; 57 }rev_hash; 58 rev_hash rev_hash_table[400]; 59 int insert_rev_hash(char* input_name,int dfa_group_pointer)//插入hash表 60 { 61 int for_i; 62 unsigned int result; 63 int counter; 64 int byte_of_table; 65 char* hash_name; 66 byte_of_table=(dfa_node_number+7)/8; 67 result=0; 68 for(for_i=0;for_i<byte_of_table;for_i++) 69 { 70 result+=(unsigned int) input_name[for_i]; 71 } 72 result=result%397; 73 counter=0; 74 while(counter<397) 75 { 76 if(rev_hash_table[result].in_use!=1) 77 { 78 rev_hash_table[result].dfa_group_index=dfa_group_pointer; 79 rev_hash_table[result].in_use=1; 80 hash_name=malloc(sizeof(char)*(byte_of_table+1)); 81 for(for_i=0;for_i<byte_of_table;for_i++) 82 { 83 hash_name[for_i]=input_name[for_i]; 84 } 85 hash_name[for_i]='\0'; 86 rev_hash_table[result].name=hash_name; 87 return result; 88 } 89 result=(result+1)%397; 90 counter++; 91 } 92 return -1; 93 } 94 int search_rev_hash(char* input_name)//根据名字来搜索hash表,如果存在则返回群标号 95 { 96 int for_i; 97 unsigned int result; 98 int counter; 99 int byte_of_table; 100 char* hash_name; 101 int compare_result; 102 byte_of_table=(dfa_node_number+7)/8; 103 result=0; 104 for(for_i=0;for_i<byte_of_table;for_i++) 105 { 106 result+=(unsigned int) input_name[for_i]; 107 } 108 result=result%397; 109 counter=0; 110 while(counter<397) 111 { 112 if(rev_hash_table[result].in_use==1) 113 { 114 compare_result=0; 115 for(for_i=0;for_i<byte_of_bitmap;for_i++) 116 { 117 compare_result+=!((rev_hash_table[result].name)[for_i]==input_name[for_i]); 118 } 119 if(compare_result==0) 120 { 121 return rev_hash_table[result].dfa_group_index; 122 } 123 else 124 { 125 result=(result+1)%397; 126 counter++; 127 } 128 } 129 else 130 { 131 if(rev_hash_table[result].in_use==2) 132 { 133 result=(result+1)%397; 134 counter++; 135 } 136 else 137 { 138 return -1; 139 } 140 } 141 } 142 return -1; 143 } 144 145 146 void map_name(char* output,int group_number)//将一个群转换为字符串 147 { 148 int for_i,for_j; 149 for(for_i=0;for_i<=(dfa_node_number+7)/8;for_i++) 150 { 151 output[for_i]='\0'; 152 } 153 for(for_i=0;for_i<dfa_node_number;for_i++) 154 { 155 if(*(*(group_table+group_number)+for_i)==1) 156 { 157 output[(for_i+7)/8]=BYTE_MASK>>(for_i%8)|output[(for_i+7)/8]; 158 } 159 } 160 } 161 void delete_rev_hash(char* input_name)//从hash表中删除一个节点 162 { 163 int for_i; 164 unsigned int result; 165 int counter; 166 int byte_of_table; 167 char* hash_name; 168 int compare_result; 169 byte_of_table=(dfa_node_number+7)/8; 170 result=0; 171 for(for_i=0;for_i<byte_of_table;for_i++) 172 { 173 result+=(unsigned int) input_name[for_i]; 174 } 175 result=result%397; 176 counter=0; 177 while(counter<397) 178 { 179 if(rev_hash_table[result].in_use==1) 180 { 181 compare_result=0; 182 for(for_i=0;for_i<byte_of_bitmap;for_i++) 183 { 184 compare_result+=!((rev_hash_table[result].name)[for_i]==input_name[for_i]); 185 } 186 if(compare_result==0) 187 { 188 rev_hash_table[result].in_use=2; 189 } 190 else 191 { 192 result=(result+1)%397; 193 counter++; 194 } 195 } 196 else 197 { 198 if(rev_hash_table[result].in_use==2) 199 { 200 result=(result+1)%397; 201 counter++; 202 } 203 } 204 } 205 } 206 207 char* group_transition(int group_number,char input_label)//处理一个群在一个字母上的转移,结果以一个字符串来表示 208 { 209 char* result_return; 210 int for_i,for_j,for_k; 211 int temp_destination; 212 pdfa_edge temp_edge; 213 result_return=malloc(sizeof(char)*((dfa_node_number+7)/8+1)); 214 for(for_i=0;for_i<=(dfa_node_number+7)/8;for_i++) 215 { 216 result_return[for_i]='\0'; 217 } 218 for(for_i=0;for_i<dfa_node_number;for_i++) 219 { 220 if(*(*(group_table+group_number)+for_i)==1) 221 { 222 temp_edge=current_dfa_table[for_i+1].begin; 223 while((temp!=NULL)&&(temp->label!=input_label)) 224 { 225 temp=temp->next; 226 } 227 if(temp!=NULL) 228 { 229 temp_destination=temp->destination_number; 230 temp_destination--;//注意这里要减1 231 result_return[(temp_destination+7)/8]=result_return[(temp_destination+7)/8]|BYTE_MASK>>(temp_destination%8); 232 } 233 } 234 } 235 return result_return; 236 } 237 238 void intersect_group(int dest_group,int origin_group,char in_label) 239 { 240 int for_i,for_j,for_k; 241 char* dest;//最后用来注册hash表的名字 242 int* dest_number;//用来表示哪些是目标地址 243 pdfa_edge temp_edge;//用来遍历邻接表 244 int temp_dest_number; 245 dest=malloc(sizeof(char)*((dfa_node_number+7)/8+1)); 246 dest_number=malloc(sizeof(int)*dfa_node_number); 247 *(group_table+group_index)=dest_number;//复制目标地址 248 for(for_i=0;for_i<dfa_node_number;for_i++) 249 { 250 dest_number[for_i]=0; 251 }//初始化 252 for(for_i=0;for_i<=(dfa_node_number+7)/8;for_i++) 253 { 254 dest[for_i]='\0'; 255 }//初始化为0 256 group_index++;//建立一个新的群 257 for(for_i=0;for_i<dfa_node_number;for_i++) 258 { 259 if(*(*(group_table+origin_group)+for_i)==1) 260 { 261 temp_edge=rev_dfa_table[for_i=1];//注意加1 262 while(temp_edge!=NULL) 263 { 264 if(temp_edge->label==in_label) 265 { 266 temp_dest_number=temp_edge->destination_number; 267 temp_dest_number--; 268 dest_number[temp_dest_number]=1; 269 } 270 temp_edge=temp_edge->next; 271 } 272 } 273 }//遍历邻接表完成 274 //然后取交集 275 for(for_i=0;for_i<dfa_node_number;for_i++) 276 { 277 if(*(*(group_table+dest)+for_i)==0) 278 { 279 dest_number[temp_dest_number]=0; 280 } 281 }//交集处理完成 282 for(for_i=0;for_i<dfa_node_number;for_i++) 283 { 284 if(dest_number[for_i]==1) 285 { 286 dest[for_i/8]=dest[for_i/8]|(BYTE_MASK>>for_i%8); 287 belong_to[for_i]=group_index; 288 } 289 }//名字及相应的群建立完成 290 insert_rev_hash(dest,group_index);//插入hash表中 291 free(dest);//释放内存空间 292 } 293 294 295 296 297 298 299 300 void group_min_dfa(void) 301 { 302 char* temp_name; 303 int for_i,for_j; 304 pgroup_list before,current;//用来遍历整个群链表 305 int is_group_changed;//用来标志当前是否生成了新的群号,如果没有生成,则可以结束集合分割了 306 int* is_already_tackled=malloc(sizeof(int)*dfa_node_size;//这个是用来标志在处理分割集的时候哪些点已经用过了 307 belongto=malloc(sizeof(int)*dfa_node_number); 308 group_table=malloc(sizeof(int)*100);//这里可能有点小,但是就这么着吧,不够用了自己改 309 group_index++; 310 *(group_table+group_index)=malloc(sizeof(int)*dfa_node_number);//这个是用来标识属于接受节点的那些点 311 for(for_i=1;for_i<=dfa_node_number;for_i++)//初始化那些属于开始节点的群 312 { 313 if(current_dfa_table[for_i].is_end==1) 314 { 315 *(*(group_table+group_index)+for_i-1)=1;//注意这里要减一 316 *(belong_to+for_i-1)=group_index; 317 } 318 else 319 { 320 *(*(group_table+group_index)+for_i-1)=0; 321 } 322 } 323 temp_name=malloc(sizeof(char)*((dfa_node_number+7)/8+1)); 324 map_name(temp_name,group_index); 325 insert_rev_hash(temp_name,group_index); 326 insert_group(group_index); 327 group_index++; 328 *(group_table+group_index)=malloc(sizeof(int)*dfa_node_number);//这个是用来标识属于非接受节点的那些点 329 for(for_i=1;for_i<=dfa_node_number;for_i++)//初始化那些不是接受节点所在的群 330 { 331 if(current_dfa_table[for_i].is_end==0) 332 { 333 *(*(group_table+group_index)+for_i-1)=1;//注意这里要减一 334 *(belong_to+for_i-1)=group_index; 335 } 336 else 337 { 338 *(*(group_table+group_index)+for_i-1)=0; 339 } 340 } 341 temp_name=malloc(sizeof(char)*((dfa_node_number+7)/8+1)); 342 map_name(temp_name,group_index); 343 insert_rev_hash(temp_name,group_index); 344 insert_group_index; 345 //初始化工作完成 346 is_group_changed=1; 347 while(is_group_changed!=0)//现在开始遍历工作 348 { 349 is_group_changed=0; 350 before=group_list_begin; 351 current=before->next; 352 while(current!=NULL)//现在开始遍历整个群链表 353 { 354 int label_traverse; 355 char* name_for_transition; 356 for(label_traverse=0;label_traverse<alpha_size;label_traverse++) 357 { 358 name_for_transition=group_transition(current->group_number,mini_alpha_set[label_traverse]); 359 //这里进行边的转换,并为目标集合生成一个名字 360 int search_hash_result=search_rev_hash(name_for_transition);//搜索这个名字是不是已经在群里面了 361 if(search_hash_result==-1)//如果这个名字当前不存在,则需要分割 362 { 363 is_group_changed=1;//标志为已经改变 364 //记录这个name里面包含的群,然后每个群再通过反向调整,并与当前群相交来建立新的群 365 for(int for_k=0;for_k<dfa_node_number;for_k++) 366 { 367 is_already_tackled[for_k]=0;//初始化为未使用 368 } 369 for(int for_k=0;for_k<dfa_node_number;for_k++)//处理每一位 370 { 371 if(is_already_tackled[for_k]==0) 372 { 373 is_already_tackled[for_k]=1; 374 if(name_for_transition[(for_k+7)/8]&(BYTE_MASK>>(for_k%8)))//如果这里被置位了 375 { 376 int temp_group_number=belong_to[for_k]; 377 for(int for_m=for_k;for_m<dfa_node_number;for_m++) 378 { 379 if(belong_to[for_m]==temp_group_number) 380 { 381 is_already_tackled[for_m]=1; 382 } 383 }//把属于一个群的全都标记为已经处理 384 //然后在对这个群与当前群来处理 385 intersect_group(current->group_number,temp_group_number,mini_alpha_set[label_traverse]); 386 //前面的函数来处理交集,并生成一个新的群,自动增加到hash中 387 if(before==group_list_begin)//插入群的时候需要考虑这个情况 388 { 389 pgroup_list temp_group_add=malloc(sizeof(struct _group_list)); 390 temp_group_add->group_number=group_index; 391 temp_group_add->next=group_list_begin->next; 392 group_list_begin=temp_group_add; 393 before=temp_group_add; 394 } 395 else 396 { 397 pgroup_list temp_group_add=malloc(sizeof(struct _group_list)); 398 temp_group_add->group_number=group_index; 399 temp_group_add->next=group_list_begin->next; 400 group_list_begin=temp_group_add; 401 } 402 403 }//相交群处理完成 404 }//这个位处理完成 405 }//所有位处理完成 406 delete_hash(current->group_number);//从hash表中取消这个群名字的注册 407 free(*(group_table+current->group_number);//释放这个名字所占的全局表空间 408 *(group_table+current->group_number)=NULL;//一定要列为空 409 current=current->next;//处理下一个群 410 free(before->next);//释放空间 411 before->next=current;//这样就把当前处理的群从当前链表中删去了 412 break;//下面的字符就不需要处理了,直接跳出循环,处理下一个群标号 413 }//当前群分割完成 414 free(name_for_transition);//释放临时申请的内存 415 }//当前群处理完成 416 }//当前遍历完成 417 }//没有额外的群加入,所有的处理完成 418 419 //现在所有的群都建造完成了 420 //现在的任务是重新建立dfa表 421 //这里需要确定哪些是开始节点,那些是结束节点,并做好标注 422 //具体代码就不详细写了,太尼玛累了 423 }