• nfa转dfa,正式完成


    为了加速转换的处理,我压缩了符号表。具体算法参考任何一本与编译或者自动机相关的书籍。

    这里的核心问题是处理传递性闭包,transitive closure,这个我目前采取的是最简单的warshall算法,虽然是4次的复杂度,但是由于我构建nfa的时候并没有采取标准的方法,使得nfa的节点减少很多。ps,上上篇所说的re转nfa,我这里有一个修改,就是对于or转换,不再增加节点,而是只增加两条空转换边。

    相关代码如下

      1 #include "nfa_process.h"
      2 //首先在原来的nfa节点中,把最后的那个正则的开始节点可达的那些节点提取出来,相当于又一次的拷贝
      3 p_edge_list nfa_to_dfa[100];//这个当作新的nfa图
      4 int nfa_min_node_number=0;//注意nfa_min_node_number使用的时候是从1开始的,他的值表示已经使用到了多少
      5 #define BYTE_MASK 0x80
      6 typedef struct _dfa_edge
      7 {
      8     struct _dfa_edge* next;
      9     char label;
     10     int dest_dfa_index;
     11 }dfa_edge,*pdfa_edge;
     12 typedef struct _dfa_node
     13 {
     14     pdfa_edge begin;
     15     int dfa_hash_number;
     16 }dfa_node,*pdfa_node;
     17 dfa_node current_dfa_table[400];//由于dfa的数量很大,所以开一个400的数组
     18 int dfa_node_number=0;//dfa_node_number作为一个全局变量,用来标号使用
     19 int current_dfa_node=0;//这个用来表示处理到了那一个dfa节点了
     20 typedef struct _dfa_hash
     21 {
     22     int in_use;
     23     char* name;
     24     int dfa_node_pointer;
     25 }dfa_hash;
     26 dfa_hash dfa_hash_table[400];//400的理由同上,这里刚好397是一个质数
     27 void ntd_add_edge(int ntd_node_begin,int ntd_node_end,char label)//在压缩过的 nfa中加边 
     28 {
     29     p_edge_list temp_pnode=malloc(sizeof(struct _graph_edge_list));
     30     temp_pnode->label_of_edge=label;
     31     temp_pnode->destination_number=ntd_node_end;
     32     temp_pnode->next_edge=nfa_to_dfa[ntd_node_begin];
     33     nfa_to_dfa[ntd_node_begin]=temp_pnode;
     34 }
     35 int dfa_into_hash(char* input_name,int dfa_node_index,int byte_of_bitmap)//这里是hash插入函数
     36 {
     37     int for_i;
     38     unsigned int result;
     39     int counter;
     40     char* hash_name;
     41     result=0;
     42     for(for_i=0;for_i<byte_of_bitmap;for_i++)
     43     {
     44         result+=(unsigned int) input_name[for_i];
     45     }
     46     result=result%397;
     47     counter=0;
     48     while(counter<397)
     49     {
     50         if(dfa_hash_table[result].in_use==0)
     51         {
     52             dfa_hash_table[result].dfa_node_pointer=dfa_node_index;
     53             dfa_hash_table[result].in_use=1;
     54             hash_name=malloc(sizeof(char)*(byte_of_bitmap+1));
     55             strcpy(hash_name,input_name);
     56             dfa_hash_table[result].name=hash_name;
     57             return result;
     58         }
     59         result=(result+1)%397;
     60         counter++;
     61     }
     62     return -1;
     63 }
     64 int search_dfa_hash(char* input_name)//对于一个名字寻找是否已经在表中
     65 {
     66     int for_i;
     67     unsigned int result;
     68     int counter;
     69     int byte_of_bitmap=strlen(input_name);
     70     result=0;
     71     for(for_i=0;for_i<byte_of_bitmap;for_i++)
     72     {
     73         result+=(unsigned int) input_name[for_i];
     74     }
     75     result=result%397;
     76     counter=0;
     77     while(counter<397)
     78     {
     79         if(dfa_hash_table[result].in_use==0)
     80         {
     81             return -1;
     82         }
     83         else
     84         {
     85             if(!strcmp(dfa_hash_table[result].name,input_name))
     86             {
     87                 return result;
     88             }
     89             else
     90             {
     91                 result=(result+1)%397;
     92                 counter++;
     93             }
     94         }
     95     }
     96     return -1;
     97 }
     98 
     99 
    100 int hamming_distance(int input)//用来得到一个4字节的int里面二进制存在1的个数
    101 {
    102     int temp=(unsigned)input;
    103     temp=(temp&0x55555555)+((temp>>1)&0x55555555);
    104     temp=(temp&0x33333333)+((temp>>2)&0x33333333);
    105     temp=(temp&0x0f0f0f0f)+((temp>>4)&0x0f0f0f0f);
    106     temp=(temp&0x00ff00ff)+((temp>>8)&0x00ff00ff);
    107     temp=(temp&0x0000ffff)+((temp>>16)&0x0000ffff);
    108     return temp;
    109 }
    110 int length_of_char_set(void)//计算字符表中的字符的种类
    111 {
    112     int result=0;
    113     result+=hamming_distance(alpha_table.char_one);
    114     result+=hamming_distance(alpha_table.char_two);
    115     result+=hamming_distance(alpha_table.char_three);
    116     result+=hamming_distance(alpha_table.char_four);
    117     return result;
    118 }
    119 
    120  minimize_char_set(char* mini_set)//压缩字母表
    121 {
    122     int for_i;
    123     unsigned int another_mask;
    124     int mini_set_counter;
    125     mini_set_counter=for_i=0;
    126     for(for_i=0;for_i<32;for_i++)
    127     {
    128         if(for_i!=17)//对于空转换字符我们不予理睬
    129         {
    130             another_mask=MASK>>for_i;
    131             if(alpha_table.char_one&another_mask)
    132             {
    133                 
    134                 mini_set[mini_set_counter++]=for_i;
    135             }
    136         }
    137     }
    138     for(for_i=32;for_i<64;for_i++)
    139     {
    140         another_mask=MASK>>(for_i%32);
    141         if(alpha_table.char_two&another_mask)
    142         {
    143             mini_set[mini_set_counter++]=for_i;
    144         }
    145     }
    146     for(for_i=64;for_i<96;for_i++)
    147     {
    148         another_mask=MASK>>(for_i%32);
    149         if(alpha_table.char_three&another_mask)
    150         {
    151             mini_set[mini_set_counter++]=for_i;
    152         }
    153     }
    154     for(for_i=96;for_i<128;for_i++)
    155     {
    156         another_mask=MASK>>(for_i%32);
    157         if(alpha_table.char_four&another_mask)
    158         {
    159             mini_set[mini_set_counter++]=for_i;
    160         }
    161     }
    162 }
    163 
    164 void set_dfa_bit(char* begin,int index)//在转换节点中设置第index位,这里是从1开始数的,所以要注意
    165 {
    166     char* temp;
    167     index=index-1;
    168     temp=begin+index/8;
    169     index=index%8;
    170     *temp=(unsigned char)(*temp)|(BYTE_MASK>>index);
    171 }
    172 int extract_nfa(void)//这里是压缩nfa,实质上与前面那个nfa复制函数是一样的 
    173 //返回开始节点的索引,为了nfa转dfa用
    174 {
    175     int nfa_begin_number;
    176     int nfa_end_number;
    
    177     int copy_destination;
    178     int offset;
    179     int original_token;
    180     char copy_label;
    181     p_edge_list pcopy;
    182     original_token=token_node_number-1;
    183     nfa_begin_number=token_node[original_token].bottom;
    184     nfa_end_number=token_node[original_token].top;
    185     offset=nfa_min_node_number-nfa_begin_number+1;//因为这样要考虑下一个节点
    186     for(nfa_begin_number;nfa_begin_number<=nfa_end_number;nfa_begin_number++)//开始复制图
    187     {
    188         pcopy=nfa_node[nfa_begin_number];
    189         nfa_min_node_number++;
    190         nfa_node[nfa_min_node_number]=NULL;
    191         while(pcopy!=NULL)
    192         {
    193             copy_label=pcopy->label_of_edge;
    194             copy_destination=pcopy->destination_number+offset;
    195             ntd_add_edge(nfa_min_node_number,copy_destination,copy_label);
    196             pcopy=pcopy->next_edge;
    197         }
    198     }
    199     return token_node[original_token].begin+offset;
    200 }
    201 void tackle_dfa_label(char* output,int dfa_current,char input_label,int node_size)//处理边转换
    202 {
    203     char* current_nfa_set;
    204     int for_i;
    205     int dfa_hash_index;
    206     p_edge_list temp_edge;//这里处理的还是nfa的边
    207     char temp_label=input_label;
    208     dfa_hash_index=current_dfa_table[dfa_current].dfa_hash_number;
    209     current_nfa_set=dfa_hash_table[dfa_hash_index].name;
    210     for(for_i=0;for_i<node_size;for_i++)//对于这个位图中的每一位进行遍历
    211     {
    212         if((BYTE_MASK>>(for_i%8))&(current_nfa_set[for_i/8]))//如果这个位有效
    213         {
    214             temp_edge=nfa_to_dfa[for_i+1];//注意这里要加1,因为 nfa_to_table的索引是从1开始的
    215             while(temp_edge!=NULL)
    216             {
    217                 if(temp_edge->label_of_edge==temp_label)
    218                 {
    219                     set_dfa_bit(output,temp_edge->destination_number);//注意这里不需要减1
    220                 }
    221                 temp_edge=temp_edge->next_edge;
    222             }
    223         }
    224     }
    225 }
    226 
    227 
    228 int is_label_null(char* input_name,int name_size_inbyte)//判断一个label是否为空
    229 {
    230     int result;
    231     int for_i;
    232     result=0;
    233     for(for_i=0;for_i<name_size_inbyte;for_i++)
    234     {
    235         result+=(unsigned char)(input_name[for_i]);
    236     }
    237     if(result==0)
    238     {
    239         return 1;
    240     }
    241     else
    242     {
    243         return 0;
    244     }
    245 }
    246 void extend_dfa_label(char* input_name,int node_size,int* result_null_access)//这里对结果进行空扩展
    247 {
    248     char* extend_temp;
    249     int for_i,for_j;
    250     int size_in_byte;
    251     unsigned char current_mask;
    252     int for_k;
    253     size_in_byte=(node_size+7)/8;
    254     for_j=0;
    255     extend_temp=malloc(sizeof(char)*(size_in_byte));//临时的位图
    256     for(for_i=0;for_i<size_in_byte;for_i++)
    257     {
    258         extend_temp[for_i]=0;
    259     }
    260     while(for_j<node_size)
    261     {
    262         current_mask=BYTE_MASK>>(for_j%8);
    263         if(input_name[for_j/8]&current_mask)
    264         {
    265             for(for_k=0;for_k<node_size;for_k++)
    266             {
    267                 if(result_null_access[for_j*node_size+for_k])
    268                 {
    269                     set_dfa_bit(extend_temp,for_k+1);//for_k处在for_k+1位那里
    270                 }
    271             }
    272         }
    273         for_j++;
    274     }
    275     for(for_k=0;for_k<size_in_byte;for_k++)//然后把结果复制回去
    276     {
    277         input_name[for_k]=extend_temp[for_k];
    278     }
    279     free(extend_temp);
    280 }
    281 
    282 void null_transition(int index_of_begin)//这里是nfa转dfa的主函数 
    283 {
    284     int nfa_begin=index_of_begin;//获得起始节点
    285     int node_size=nfa_min_node_number;
    286     int for_i,for_k,for_m;
    287     int for_j;
    288     //现在需要求传递性闭包,目前为了省事采取四次方的算法,虽然可以做点优化,但是是以空间复杂度为代价的
    289     //由于是四次方的算法,所以我们需要尽量减少节点的数目,幸运的是,我自己定义的转换规则几乎很少去增加节点
    290     //这样将大大的有利于效率
    291     //计算传递性闭包,我采用warshall算法,直接开一个n*n的矩阵
    292     int* origin_null_access=malloc(sizeof(int)*node_size*node_size);
    293     int* result_null_access=malloc(sizeof(int)*node_size*node_size);
    294     int node_size_byte=(node_size+7)/8;
    295     int alpha_size;
    296     char* mini_alpha_set;
    297     int search_result;
    298     char label;
    299     pdfa_edge temp_dfa_edge;
    300     char* begin_nfa_set=malloc(sizeof(char)*(node_size_byte+1));
    301     char* temp_nfa_set=malloc(sizeof(char)*(node_size_byte+1));
    302     for(for_i=0;for_i<node_size*node_size;for_i++)
    303     {
    304         origin_null_access[for_i]=0;
    305         result_null_access[for_i]=0;
    306     }
    307     for(for_i=0;for_i<=node_size_byte;for_i++)//malloc注意一定要自己去初始化值,否则默认为0xcd操
    308     {
    309         begin_nfa_set[for_i]=0;
    310         temp_nfa_set[for_i]=0;
    311     }
    312     for(for_i=1;for_i<=node_size;for_i++)//初始化矩阵
    313         //注意矩阵下标是从0开始的,而节点下标是从1开始的,引用的时候要小心
    314     {
    315         p_edge_list temp;
    316         temp=nfa_to_dfa[for_i];
    317         origin_null_access[(for_i-1)*node_size+for_i-1]=1;//由于自己对自己总是可达的
    318         result_null_access[(for_i-1)*node_size+for_i-1]=1;//同上
    319         while(temp!=NULL)
    320         {
    321             if(temp->label_of_edge==(char)17)
    322             {
    323                 origin_null_access[(for_i-1)*node_size+temp->destination_number-1]=1;
    324                 result_null_access[(for_i-1)*node_size+temp->destination_number-1]=1;
    325             }
    326             temp=temp->next_edge;
    327         }
    328     }
    329     //初始化基础的可达矩阵
    330     for(for_i=1;for_i<node_size;for_i++)//这里之所以迭代node_size-1次,因为最长简单路径不超过node_size-1条边。
    331     {
    332         for(for_j=0;for_j<node_size;for_j++)
    333         {
    334             for(for_k=0;for_k<node_size;for_k++)
    335             {
    336                 int temp=0;
    337                 for(for_m=0;for_m<node_size;for_m++)
    338                 {
    339                     temp+=result_null_access[for_j*node_size+for_m]*origin_null_access[for_m*node_size+for_k];
    340                 }
    341                 if(temp>0)
    342                 {
    343                     result_null_access[for_j*node_size+for_k]=1;//可联通
    344                 }
    345                 else
    346                 {
    347                     result_null_access[for_j*node_size+for_k]=0;//不可联通
    348                 }
    349             }
    350         }
    351     }
    352     //至此邻接矩阵已经构建完成,现在我们把它变成邻接位图
    353     node_size_byte=(node_size+7)/8;
    354     //现在来处理字符集,注意,这里不包括空转换字符
    355     alpha_size=length_of_char_set();
    356     mini_alpha_set=malloc(sizeof(char)*alpha_size);
    357     for(for_i=0;for_i<alpha_size;for_i++)
    358     {
    359         mini_alpha_set[for_i]=0;
    360     }
    361     minimize_char_set(mini_alpha_set);
    362     //这里用位图来表示每个点的在某一个字符上的转换集合
    363     //加上一个,是为了使他变成字符串,这样就好比较了
    364     //方便了hash和二叉树的插入和寻找
    365     for(for_i=0;for_i<node_size;for_i++)
    366     {
    367         if(result_null_access[(nfa_begin-1)*node_size+for_i]==1)//我擦,这里忘了减少1了
    368         {
    369             set_dfa_bit(begin_nfa_set,for_i+1);
    370         }
    371     }
    372     dfa_node_number++;
    373     current_dfa_table[dfa_node_number].begin=NULL;
    374     current_dfa_table[dfa_node_number].dfa_hash_number=dfa_into_hash(begin_nfa_set,dfa_node_number,node_size_byte+1);
    375     current_dfa_node=0;
    376     while(current_dfa_node<dfa_node_number)
    377     {
    378         current_dfa_node++;
    379         
    380         for(for_j=0;for_j<alpha_size;for_j++)
    381         {
    382             for(for_i=0;for_i<=node_size_byte;for_i++)
    383             {
    384                 temp_nfa_set[for_i]='';
    385             }//清空上次的结果 
    386             label=mini_alpha_set[for_j];//设定转换条件
    387             tackle_dfa_label(temp_nfa_set,current_dfa_node,label,node_size);//进行边转换
    388             extend_dfa_label(temp_nfa_set,node_size,result_null_access);//进行空扩展
    389             if(!is_label_null(temp_nfa_set,node_size_byte))//如果在这个字符上有转换
    390             {
    391                 search_result=search_dfa_hash(temp_nfa_set);
    392                 if(search_result==-1)//如果为新的状态,则为之建立一个新的节点
    393                 {
    394                     dfa_node_number++;
    395                     current_dfa_table[dfa_node_number].begin=NULL;
    396                     current_dfa_table[dfa_node_number].dfa_hash_number=dfa_into_hash(temp_nfa_set,dfa_node_number,node_size_byte+1);
    397                     temp_dfa_edge=malloc(sizeof(struct _dfa_edge));//建立一条新的边
    398                     temp_dfa_edge->next=current_dfa_table[current_dfa_node].begin;
    399                     temp_dfa_edge->label=mini_alpha_set[for_j];
    400                     temp_dfa_edge->dest_dfa_index=dfa_node_number;
    401                     printf("add an node %d
    ",dfa_node_number);
    402                     printf("add an edge from %d to %d with label %c
    ",current_dfa_node,dfa_node_number,mini_alpha_set[for_j]);
    403                 }
    404                 else//如果已经存在这个状态
    405                 {
    406                     temp_dfa_edge=malloc(sizeof(struct _dfa_edge));
    407                     temp_dfa_edge->next=current_dfa_table[current_dfa_node].begin;
    408                     temp_dfa_edge->label=mini_alpha_set[for_j];
    409                     temp_dfa_edge->dest_dfa_index=dfa_hash_table[search_result].dfa_node_pointer;
    410                     printf("add an edge from %d to %d with label %c
    ",current_dfa_node,temp_dfa_edge->dest_dfa_index,mini_alpha_set[for_j]);
    411                 }
    412                 current_dfa_table[current_dfa_node].begin=temp_dfa_edge;//修改邻接表 
    413             }//对于单字符处理完毕
    414         }//对于所有的字符都处理完毕了
    415     }//对于当前节点处理完毕,但是可能还有其他节点,继续迭代。
    416 }//他妈的 nfa转dfa全都处理完毕 
    417 void show_dfa_table(void)//输出邻接表
    418 { 
    419     int for_i;
    420     pdfa_edge temp_dfa_edge;
    421     for(for_i=1;for_i<=dfa_node_number;for_i++)
    422     {
    423         temp_dfa_edge=current_dfa_table[for_i].begin;
    424         while(temp_dfa_edge!=NULL)
    425         {
    426             printf("there is an dfa edge from %d to %d with label %c
    ",for_i,temp_dfa_edge->dest_dfa_index,temp_dfa_edge->label);
    427             temp_dfa_edge=temp_dfa_edge->next;
    428         }
    429     }
    430 }
    431 
    432 
    433 
    434 
    435 
    436     
  • 相关阅读:
    使用GetLogicalProcessorInformation获取逻辑处理器的详细信息(NUMA节点数、物理CPU数、CPU核心数、逻辑CPU数、各级Cache)
    Android学习-应用程序管理
    用户过2亿获取每个用户不到6分钱,闪传是怎么做到?(最大的成本是决策成本,否则全是无用功)
    Delphi应用程序的调试(十)调试器选项(在IDE中不要使用异常)
    无标题窗体拖动(三种方法)
    关于Qt在子线程中使用QMessageBox的折衷方法
    Qt自定义事件的实现(军队真正干活,但要增加监军,大平台通知事件,事件内容自定义)
    java对数据库的操作
    ddd
    伟大是熬出来的
  • 原文地址:https://www.cnblogs.com/huangfeidian/p/3156863.html
Copyright © 2020-2023  润新知