nfa转dfa,正式完成
为了加速转换的处理,我压缩了符号表。具体算法参考任何一本与编译或者自动机相关的书籍。
这里的核心问题是处理传递性闭包,transitive closure,这个我目前采取的是最简单的warshall算法,虽然是4次的复杂度,但是由于我构建nfa的时候并没有采取标准的方法,使得nfa的节点减少很多。ps,上上篇所说的re转nfa,我这里有一个修改,就是对于or转换,不再增加节点,而是只增加两条空转换边。
相关代码如下
#include "nfa_process.h"
//首先在原来的nfa节点中,把最后的那个正则的开始节点可达的那些节点提取出来,相当于又一次的拷贝
p_edge_list nfa_to_dfa[];//这个当作新的nfa图
int nfa_min_node_number=;//注意nfa_min_node_number使用的时候是从1开始的,他的值表示已经使用到了多少
#define BYTE_MASK 0x80
typedef struct _dfa_edge
{
struct _dfa_edge* next;
char label;
int dest_dfa_index;
}dfa_edge,*pdfa_edge;
typedef struct _dfa_node
{
pdfa_edge begin;
int dfa_hash_number;
}dfa_node,*pdfa_node;
dfa_node current_dfa_table[];//由于dfa的数量很大,所以开一个400的数组
int dfa_node_number=;//dfa_node_number作为一个全局变量,用来标号使用
int current_dfa_node=;//这个用来表示处理到了那一个dfa节点了
typedef struct _dfa_hash
{
int in_use;
char* name;
int dfa_node_pointer;
}dfa_hash;
dfa_hash dfa_hash_table[];//400的理由同上,这里刚好397是一个质数
void ntd_add_edge(int ntd_node_begin,int ntd_node_end,char label)//在压缩过的 nfa中加边
{
p_edge_list temp_pnode=malloc(sizeof(struct _graph_edge_list));
temp_pnode->label_of_edge=label;
temp_pnode->destination_number=ntd_node_end;
temp_pnode->next_edge=nfa_to_dfa[ntd_node_begin];
nfa_to_dfa[ntd_node_begin]=temp_pnode;
}
int dfa_into_hash(char* input_name,int dfa_node_index,int byte_of_bitmap)//这里是hash插入函数
{
int for_i;
unsigned int result;
int counter;
char* hash_name;
result=;
for(for_i=;for_i<byte_of_bitmap;for_i++)
{
result+=(unsigned int) input_name[for_i];
}
result=result%;
counter=;
while(counter<)
{
if(dfa_hash_table[result].in_use==)
{
dfa_hash_table[result].dfa_node_pointer=dfa_node_index;
dfa_hash_table[result].in_use=;
hash_name=malloc(sizeof(char)*(byte_of_bitmap+));
strcpy(hash_name,input_name);
dfa_hash_table[result].name=hash_name;
return result;
}
result=(result+)%;
counter++;
}
return -;
}
int search_dfa_hash(char* input_name)//对于一个名字寻找是否已经在表中
{
int for_i;
unsigned int result;
int counter;
int byte_of_bitmap=strlen(input_name);
result=;
for(for_i=;for_i<byte_of_bitmap;for_i++)
{
result+=(unsigned int) input_name[for_i];
}
result=result%;
counter=;
while(counter<)
{
if(dfa_hash_table[result].in_use==)
{
return -;
}
else
{
if(!strcmp(dfa_hash_table[result].name,input_name))
{
return result;
}
else
{
result=(result+)%;
counter++;
}
}
}
return -;
} int hamming_distance(int input)//用来得到一个4字节的int里面二进制存在1的个数
{
int temp=(unsigned)input;
temp=(temp&0x55555555)+((temp>>)&0x55555555);
temp=(temp&0x33333333)+((temp>>)&0x33333333);
temp=(temp&0x0f0f0f0f)+((temp>>)&0x0f0f0f0f);
temp=(temp&0x00ff00ff)+((temp>>)&0x00ff00ff);
temp=(temp&0x0000ffff)+((temp>>)&0x0000ffff);
return temp;
}
int length_of_char_set(void)//计算字符表中的字符的种类
{
int result=;
result+=hamming_distance(alpha_table.char_one);
result+=hamming_distance(alpha_table.char_two);
result+=hamming_distance(alpha_table.char_three);
result+=hamming_distance(alpha_table.char_four);
return result;
} minimize_char_set(char* mini_set)//压缩字母表
{
int for_i;
unsigned int another_mask;
int mini_set_counter;
mini_set_counter=for_i=;
for(for_i=;for_i<;for_i++)
{
if(for_i!=)//对于空转换字符我们不予理睬
{
another_mask=MASK>>for_i;
if(alpha_table.char_one&another_mask)
{ mini_set[mini_set_counter++]=for_i;
}
}
}
for(for_i=;for_i<;for_i++)
{
another_mask=MASK>>(for_i%);
if(alpha_table.char_two&another_mask)
{
mini_set[mini_set_counter++]=for_i;
}
}
for(for_i=;for_i<;for_i++)
{
another_mask=MASK>>(for_i%);
if(alpha_table.char_three&another_mask)
{
mini_set[mini_set_counter++]=for_i;
}
}
for(for_i=;for_i<;for_i++)
{
another_mask=MASK>>(for_i%);
if(alpha_table.char_four&another_mask)
{
mini_set[mini_set_counter++]=for_i;
}
}
} void set_dfa_bit(char* begin,int index)//在转换节点中设置第index位,这里是从1开始数的,所以要注意
{
char* temp;
index=index-;
temp=begin+index/;
index=index%;
*temp=(unsigned char)(*temp)|(BYTE_MASK>>index);
}
int extract_nfa(void)//这里是压缩nfa,实质上与前面那个nfa复制函数是一样的
//返回开始节点的索引,为了nfa转dfa用
{
int nfa_begin_number;
int nfa_end_number; int copy_destination;
int offset;
int original_token;
char copy_label;
p_edge_list pcopy;
original_token=token_node_number-;
nfa_begin_number=token_node[original_token].bottom;
nfa_end_number=token_node[original_token].top;
offset=nfa_min_node_number-nfa_begin_number+;//因为这样要考虑下一个节点
for(nfa_begin_number;nfa_begin_number<=nfa_end_number;nfa_begin_number++)//开始复制图
{
pcopy=nfa_node[nfa_begin_number];
nfa_min_node_number++;
nfa_node[nfa_min_node_number]=NULL;
while(pcopy!=NULL)
{
copy_label=pcopy->label_of_edge;
copy_destination=pcopy->destination_number+offset;
ntd_add_edge(nfa_min_node_number,copy_destination,copy_label);
pcopy=pcopy->next_edge;
}
}
return token_node[original_token].begin+offset;
}
void tackle_dfa_label(char* output,int dfa_current,char input_label,int node_size)//处理边转换
{
char* current_nfa_set;
int for_i;
int dfa_hash_index;
p_edge_list temp_edge;//这里处理的还是nfa的边
char temp_label=input_label;
dfa_hash_index=current_dfa_table[dfa_current].dfa_hash_number;
current_nfa_set=dfa_hash_table[dfa_hash_index].name;
for(for_i=;for_i<node_size;for_i++)//对于这个位图中的每一位进行遍历
{
if((BYTE_MASK>>(for_i%))&(current_nfa_set[for_i/]))//如果这个位有效
{
temp_edge=nfa_to_dfa[for_i+];//注意这里要加1,因为 nfa_to_table的索引是从1开始的
while(temp_edge!=NULL)
{
if(temp_edge->label_of_edge==temp_label)
{
set_dfa_bit(output,temp_edge->destination_number);//注意这里不需要减1
}
temp_edge=temp_edge->next_edge;
}
}
}
} int is_label_null(char* input_name,int name_size_inbyte)//判断一个label是否为空
{
int result;
int for_i;
result=;
for(for_i=;for_i<name_size_inbyte;for_i++)
{
result+=(unsigned char)(input_name[for_i]);
}
if(result==)
{
return ;
}
else
{
return ;
}
}
void extend_dfa_label(char* input_name,int node_size,int* result_null_access)//这里对结果进行空扩展
{
char* extend_temp;
int for_i,for_j;
int size_in_byte;
unsigned char current_mask;
int for_k;
size_in_byte=(node_size+)/;
for_j=;
extend_temp=malloc(sizeof(char)*(size_in_byte));//临时的位图
for(for_i=;for_i<size_in_byte;for_i++)
{
extend_temp[for_i]=;
}
while(for_j<node_size)
{
current_mask=BYTE_MASK>>(for_j%);
if(input_name[for_j/]¤t_mask)
{
for(for_k=;for_k<node_size;for_k++)
{
if(result_null_access[for_j*node_size+for_k])
{
set_dfa_bit(extend_temp,for_k+);//for_k处在for_k+1位那里
}
}
}
for_j++;
}
for(for_k=;for_k<size_in_byte;for_k++)//然后把结果复制回去
{
input_name[for_k]=extend_temp[for_k];
}
free(extend_temp);
} void null_transition(int index_of_begin)//这里是nfa转dfa的主函数
{
int nfa_begin=index_of_begin;//获得起始节点
int node_size=nfa_min_node_number;
int for_i,for_k,for_m;
int for_j;
//现在需要求传递性闭包,目前为了省事采取四次方的算法,虽然可以做点优化,但是是以空间复杂度为代价的
//由于是四次方的算法,所以我们需要尽量减少节点的数目,幸运的是,我自己定义的转换规则几乎很少去增加节点
//这样将大大的有利于效率
//计算传递性闭包,我采用warshall算法,直接开一个n*n的矩阵
int* origin_null_access=malloc(sizeof(int)*node_size*node_size);
int* result_null_access=malloc(sizeof(int)*node_size*node_size);
int node_size_byte=(node_size+)/;
int alpha_size;
char* mini_alpha_set;
int search_result;
char label;
pdfa_edge temp_dfa_edge;
char* begin_nfa_set=malloc(sizeof(char)*(node_size_byte+));
char* temp_nfa_set=malloc(sizeof(char)*(node_size_byte+));
for(for_i=;for_i<node_size*node_size;for_i++)
{
origin_null_access[for_i]=;
result_null_access[for_i]=;
}
for(for_i=;for_i<=node_size_byte;for_i++)//malloc注意一定要自己去初始化值,否则默认为0xcd操
{
begin_nfa_set[for_i]=;
temp_nfa_set[for_i]=;
}
for(for_i=;for_i<=node_size;for_i++)//初始化矩阵
//注意矩阵下标是从0开始的,而节点下标是从1开始的,引用的时候要小心
{
p_edge_list temp;
temp=nfa_to_dfa[for_i];
origin_null_access[(for_i-)*node_size+for_i-]=;//由于自己对自己总是可达的
result_null_access[(for_i-)*node_size+for_i-]=;//同上
while(temp!=NULL)
{
if(temp->label_of_edge==(char))
{
origin_null_access[(for_i-)*node_size+temp->destination_number-]=;
result_null_access[(for_i-)*node_size+temp->destination_number-]=;
}
temp=temp->next_edge;
}
}
//初始化基础的可达矩阵
for(for_i=;for_i<node_size;for_i++)//这里之所以迭代node_size-1次,因为最长简单路径不超过node_size-1条边。
{
for(for_j=;for_j<node_size;for_j++)
{
for(for_k=;for_k<node_size;for_k++)
{
int temp=;
for(for_m=;for_m<node_size;for_m++)
{
temp+=result_null_access[for_j*node_size+for_m]*origin_null_access[for_m*node_size+for_k];
}
if(temp>)
{
result_null_access[for_j*node_size+for_k]=;//可联通
}
else
{
result_null_access[for_j*node_size+for_k]=;//不可联通
}
}
}
}
//至此邻接矩阵已经构建完成,现在我们把它变成邻接位图
node_size_byte=(node_size+)/;
//现在来处理字符集,注意,这里不包括空转换字符
alpha_size=length_of_char_set();
mini_alpha_set=malloc(sizeof(char)*alpha_size);
for(for_i=;for_i<alpha_size;for_i++)
{
mini_alpha_set[for_i]=;
}
minimize_char_set(mini_alpha_set);
//这里用位图来表示每个点的在某一个字符上的转换集合
//加上一个\0,是为了使他变成字符串,这样就好比较了
//方便了hash和二叉树的插入和寻找
for(for_i=;for_i<node_size;for_i++)
{
if(result_null_access[(nfa_begin-)*node_size+for_i]==)//我擦,这里忘了减少1了
{
set_dfa_bit(begin_nfa_set,for_i+);
}
}
dfa_node_number++;
current_dfa_table[dfa_node_number].begin=NULL;
current_dfa_table[dfa_node_number].dfa_hash_number=dfa_into_hash(begin_nfa_set,dfa_node_number,node_size_byte+);
current_dfa_node=;
while(current_dfa_node<dfa_node_number)
{
current_dfa_node++; for(for_j=;for_j<alpha_size;for_j++)
{
for(for_i=;for_i<=node_size_byte;for_i++)
{
temp_nfa_set[for_i]='\0';
}//清空上次的结果
label=mini_alpha_set[for_j];//设定转换条件
tackle_dfa_label(temp_nfa_set,current_dfa_node,label,node_size);//进行边转换
extend_dfa_label(temp_nfa_set,node_size,result_null_access);//进行空扩展
if(!is_label_null(temp_nfa_set,node_size_byte))//如果在这个字符上有转换
{
search_result=search_dfa_hash(temp_nfa_set);
if(search_result==-)//如果为新的状态,则为之建立一个新的节点
{
dfa_node_number++;
current_dfa_table[dfa_node_number].begin=NULL;
current_dfa_table[dfa_node_number].dfa_hash_number=dfa_into_hash(temp_nfa_set,dfa_node_number,node_size_byte+);
temp_dfa_edge=malloc(sizeof(struct _dfa_edge));//建立一条新的边
temp_dfa_edge->next=current_dfa_table[current_dfa_node].begin;
temp_dfa_edge->label=mini_alpha_set[for_j];
temp_dfa_edge->dest_dfa_index=dfa_node_number;
printf("add an node %d\n",dfa_node_number);
printf("add an edge from %d to %d with label %c\n",current_dfa_node,dfa_node_number,mini_alpha_set[for_j]);
}
else//如果已经存在这个状态
{
temp_dfa_edge=malloc(sizeof(struct _dfa_edge));
temp_dfa_edge->next=current_dfa_table[current_dfa_node].begin;
temp_dfa_edge->label=mini_alpha_set[for_j];
temp_dfa_edge->dest_dfa_index=dfa_hash_table[search_result].dfa_node_pointer;
printf("add an edge from %d to %d with label %c\n",current_dfa_node,temp_dfa_edge->dest_dfa_index,mini_alpha_set[for_j]);
}
current_dfa_table[current_dfa_node].begin=temp_dfa_edge;//修改邻接表
}//对于单字符处理完毕
}//对于所有的字符都处理完毕了
}//对于当前节点处理完毕,但是可能还有其他节点,继续迭代。
}//他妈的 nfa转dfa全都处理完毕
void show_dfa_table(void)//输出邻接表
{
int for_i;
pdfa_edge temp_dfa_edge;
for(for_i=;for_i<=dfa_node_number;for_i++)
{
temp_dfa_edge=current_dfa_table[for_i].begin;
while(temp_dfa_edge!=NULL)
{
printf("there is an dfa edge from %d to %d with label %c\n",for_i,temp_dfa_edge->dest_dfa_index,temp_dfa_edge->label);
temp_dfa_edge=temp_dfa_edge->next;
}
}
}
nfa转dfa,正式完成的更多相关文章
- NFA转DFA - json数字识别
json的主页上,提供了number类型的符号识别过程,如下: 图片引用:http://www.json.org/json-zh.html 实际上这张图片表示的是一个状态机,只是状态没有标出来.因为这 ...
- 求子串-KPM模式匹配-NFA/DFA
求子串 数据结构中对串的5种最小操作子集:串赋值,串比较,求串长,串连接,求子串,其他操作均可在该子集上实现 数据结构中串的模式匹配 KPM模式匹配算法 基本的模式匹配算法 //求字串subStrin ...
- NFA和DFA区别
一个数据块的访问时间等于寻道时间.旋转延迟时间和数据传输时间三者之和: NFA和DFA区别: 一个状态如A,遇0可以转换到下一个状态B或C,因为选择多所以不确定,因此为不确定的有限自动机: 一个状态还 ...
- 利用子集构造法实现NFA到DFA的转换
概述 NFA非有穷自动机,即当前状态识别某个转换条件后到达的后继状态不唯一,这种自动机不便机械实现,而DFA是确定有限状态的自动机,它的状态转换的条件是确定的,且状态数目往往少于NFA,所以DFA能够 ...
- 计算理论:NFA转DFA的两种方法
本文将以两种方法实现NFA转DFA,并利用C语言实现. 方法二已利用HNU OJ系统验证,方法一迷之WA,但思路应该是对的,自试方案,测试均通过. (主要是思路,AC均浮云,大概又有什么奇怪的Case ...
- NFA与DFA
正则表达式匹配,包含两个东西,一个是表达式,一个文本. NFA(Nondeterministic Finite Automaton),不确定有穷自动机,表达式主导,NFA去吃文本,贪婪算法吃下去,如果 ...
- [编译原理代码][NFA转DFA并最小化DFA并使用DFA进行词法分析]
#include <iostream> #include <vector> #include <cstring> #include "stack" ...
- 编译原理-NFA构造DFA
本题摘自北邮的编译原理与技术. 首先,根据此图构造状态转换表 表中第一列第一行表示从第一个符号B通过任意个空转换能到达的节点,Ia表示由此行的状态数组({B,5,1}可以看作0状态)经过一个a可以到达 ...
- NFA到DFA实例
下面图使用NFA表示的状态转换图, 使用子集构造法,有如下过程, ε-closure(0) = {0, 1, 2, 3, 4, 6, 7}初始值,令为AA = {0, 1, 2, 3, 4, 6, 7 ...
随机推荐
- webservice接口的发布
使用xfire-client发布webservice接口: commons-codec-1.3.jar commons-httpclient-3.0.jar 在src 下创建META-INF/xfir ...
- springMVC在jsp传对象到后台
==============实体entity======================= package com.jb.pub.entity; import java.io.Serializable ...
- Window.navigator
定义和用法 userAgent 属性是一个只读的字符串,声明了浏览器用于 HTTP 请求的用户代理头的值. 一般来讲,它是在 navigator.appCodeName 的值之后加上斜线和 navig ...
- url中的scheme
iPhone上URL Schemes的作用为应用程序提供了一个其他应用程序或者safari可以启动他的方法. --http://blog.sina.com.cn/s/blog_5673c12f0100 ...
- CriminalIntent程序中Fragment相关内容
Activity中托管UI fragment有两种方式: 添加fragment到acitivity中 在activity代码中添加fragment 第一种方法即将fragment添加到acitivit ...
- 番茄钟App(Pomodoro Tracker)
最近为了学习Swift编程语言,写了一个番茄钟的App(Pomodoro Tracker).刚上线的1.2版本增加了Apple Watch的支持. iPhone版 Apple Watch版 如果你跟我 ...
- 使用Android Studio与ArcGIS Android SDK的开发环境部署和HelloWorld
android studio(以下简称AS)是google推荐的android专用IDE,替代目前主流的eclipse,另外arcgis也把AS作为推荐的android IDE 本文不介绍androi ...
- SCCM符合性设置
符合性设置--可以针对注册表值.脚本.文件系统.补丁更新情况进行符合性检查,除了在报表中查看结果外,还可以在CCM客户端 的 配置 中查看符合性评估结果,适合排错1.配置项目---新建针对 注册表值. ...
- SQL扫描并执行文件夹里的sql脚本
场景:项目数据库操作全部使用存储过程实现.每天都会有很多存储过程更新/增加,人工对测试环境中存储过程更新,会有一定概率出现遗漏,也麻烦!所以,需要一个工具将文件夹中所有存 储过程执行一 ...
- Codeforces Educational Codeforces Round 3 B. The Best Gift 水题
B. The Best Gift 题目连接: http://www.codeforces.com/contest/609/problem/B Description Emily's birthday ...