参考链接:http://www.cnblogs.com/zzqcn/p/3525636.html

感谢原文作者。

花了两天半时间实现并测试了算法。

按照上文的思路实现了一遍,可能是原文中有些地方描述的不是特别清楚,导致一开始测试的时候发现了各种匹配遗漏的情况,后经过自己各种努力终于解决了各种遗漏。

同时在实现过程中也遇到了各种小问题,最后都解决了,总结起来主要有四个大坑,自己实现的时候需要注意,四个坑都在代码的注释里面了。

这里的实现虽然不会有遗漏的情况,但会有同一模式串在相同的偏移多次被命中的情况,但无伤大雅,至少没有遗漏不是吗。实际应用中只需对结果做去重就好了。

测试结论:对一个101.3MB的PE,从中随机抽取长度在[16-116)Bytes的模式串16个,分别用memcmp方式和AC自动机方式进行匹配,memcmp方式耗时33秒,AC方式耗时12秒,可见优势还是比较明显的。

代码中如有哪里不对,欢迎一起讨论。

 #include <cstdlib>
#include <cstdio>
#include <cstring>
#include <stdint.h>
#include <vector>
#include <map>
#include <queue>
#include <ctime> typedef struct ACNode
{
uint64_t u64Depth;
struct ACNode *pFail;
std::map<unsigned char, struct ACNode *> *pmpGotoTab;
struct ACParrent
{
struct ACNode *pParent;
unsigned char ucCondition;
} Parent;
bool bIsMathed;
} AC_NODE, *P_AC_NODE; typedef void (__stdcall *P_AC_FOUND_CALLBACK)(const unsigned char *In_pucBuf, uint64_t In_u64EndPos, uint64_t In_u64Len); int InitACGoto(const std::vector<const std::vector<unsigned char> *> &In_vctPattern,
std::vector<P_AC_NODE> &Out_vctACNodes)
{
int iRetVal = ;
P_AC_NODE pRoot = NULL;
unsigned int uiPattIdx = ;
unsigned int uiUCharIdx = ;
uint16_t u16Idx = ; if (In_vctPattern.empty())
{
iRetVal = -;
goto fun_ret;
} pRoot = (P_AC_NODE)calloc(, sizeof(AC_NODE));
if (pRoot == NULL)
{
iRetVal = -;
goto fun_ret;
} pRoot->pmpGotoTab = new std::map<unsigned char, struct ACNode *>();
for (u16Idx = ; u16Idx <= 0xff; u16Idx ++)
pRoot->pmpGotoTab->insert(std::pair<unsigned char, struct ACNode *>((unsigned char)u16Idx, pRoot));
Out_vctACNodes.push_back(pRoot); for (uiPattIdx = ; uiPattIdx < In_vctPattern.size(); uiPattIdx ++)
{
P_AC_NODE pCurNode = pRoot;
for (uiUCharIdx = ; uiUCharIdx < In_vctPattern[uiPattIdx]->size(); uiUCharIdx ++)
{
unsigned char ucCurUChar = In_vctPattern[uiPattIdx]->at(uiUCharIdx);
if (pCurNode->pmpGotoTab->find(ucCurUChar) == pCurNode->pmpGotoTab->end()
|| (pCurNode->pmpGotoTab->find(ucCurUChar) != pCurNode->pmpGotoTab->end()
&& pCurNode->pmpGotoTab->at(ucCurUChar) == pRoot))
{
P_AC_NODE pNode = (P_AC_NODE)calloc(, sizeof(AC_NODE));
if (pNode == NULL)
{
iRetVal = -;
goto fun_ret;
} pNode->u64Depth = uiUCharIdx + ;
pNode->Parent.pParent = pCurNode;
pNode->Parent.ucCondition = ucCurUChar;
pNode->pmpGotoTab = new std::map<unsigned char, struct ACNode *>(); if (pCurNode->pmpGotoTab->find(ucCurUChar) != pCurNode->pmpGotoTab->end())
pCurNode->pmpGotoTab->erase(ucCurUChar);
pCurNode->pmpGotoTab->insert(std::pair<unsigned char, struct ACNode *>(ucCurUChar, pNode));
pCurNode = pNode;
Out_vctACNodes.push_back(pNode);
}
else
pCurNode = pCurNode->pmpGotoTab->at(ucCurUChar); if (uiUCharIdx == In_vctPattern[uiPattIdx]->size() - )
pCurNode->bIsMathed = true;
}
} fun_ret:
return iRetVal;
} int ACFail(std::vector<P_AC_NODE> &Out_vctACNodes)
{
int iRetVal = ;
std::queue<P_AC_NODE> quNodes; if (Out_vctACNodes.empty())
{
iRetVal = -;
goto fun_ret;
} quNodes.push(Out_vctACNodes[]);
while (!quNodes.empty())
{
std::map<unsigned char, struct ACNode *>::iterator itGoto;
P_AC_NODE pNode = quNodes.front();
quNodes.pop();
if (pNode->u64Depth <= )
pNode->pFail = Out_vctACNodes[];
else
{
P_AC_NODE pParentFail = pNode->Parent.pParent->pFail;
while (pParentFail->pmpGotoTab->find(pNode->Parent.ucCondition) == pParentFail->pmpGotoTab->end())
pParentFail = pParentFail->pFail;
pNode->pFail = pParentFail->pmpGotoTab->at(pNode->Parent.ucCondition);
}
for (itGoto = pNode->pmpGotoTab->begin(); itGoto != pNode->pmpGotoTab->end(); itGoto ++)
{
if (itGoto->second != Out_vctACNodes[])
quNodes.push(itGoto->second);
}
} fun_ret:
return iRetVal;
} void __stdcall ACFoundCallBack(const unsigned char *In_pucBuf, uint64_t In_u64EndPos, uint64_t In_u64Len)
{
if (In_pucBuf == NULL || In_u64Len == )
goto fun_ret; printf("<<<<<<<<<<FUCKOFF:%x\n", In_u64EndPos - In_u64Len); fun_ret:
return;
} int ACSearch(const P_AC_NODE In_pRoot, const unsigned char *In_pucBuf, uint64_t In_u64BufLen, P_AC_FOUND_CALLBACK In_pfCallBack)
{
int iRetVal = ;
P_AC_NODE pCurrent = NULL;
uint64_t u64Idx = ; if (In_pRoot == NULL || In_pucBuf == NULL || In_u64BufLen == || In_pfCallBack == NULL)
{
iRetVal = -;
goto fun_ret;
} pCurrent = In_pRoot;
for (u64Idx = ; u64Idx < In_u64BufLen;)
{
P_AC_NODE pFail = NULL;
if (pCurrent->pmpGotoTab->find(In_pucBuf[u64Idx]) != pCurrent->pmpGotoTab->end())
{
pCurrent = pCurrent->pmpGotoTab->at(In_pucBuf[u64Idx]);
//坑1,出现匹配失败时不要前进,只在匹配成功时前进
u64Idx ++;
}
else
pCurrent = pCurrent->pFail; //坑3,每个节点都需要沿着失配指针一直向上找所有匹配到的结果,而不是
//只在匹配成功时才这么做,否则会出现匹配遗漏(形如“abcd”和“bc”这样的特征串并存的情况)
pFail = pCurrent->pFail;
//坑4,一定要走到根,否则会出现匹配遗漏
while (pFail != In_pRoot)
{
if (pFail->bIsMathed)
In_pfCallBack(In_pucBuf, u64Idx, pFail->u64Depth);
pFail = pFail->pFail;
}
//坑2,不管是否匹配成功,都要判断当前节点状态,因为出现失配后的
//转移也有可能转到一个成功匹配的节点上
if (pCurrent->bIsMathed)
In_pfCallBack(In_pucBuf, u64Idx, pCurrent->u64Depth);
} fun_ret:
return iRetVal;
} void ReleaseACNodes(std::vector<P_AC_NODE> &Out_vctACNodes)
{
unsigned int uiIdx = ;
for (uiIdx = ; uiIdx < Out_vctACNodes.size(); uiIdx ++)
{
delete Out_vctACNodes[uiIdx]->pmpGotoTab;
free(Out_vctACNodes[uiIdx]);
}
Out_vctACNodes.clear();
} void main(int argc, char **argv)
{
std::vector<P_AC_NODE> vctNodes;
std::vector<const std::vector<unsigned char> *> vctPatterns;
unsigned char *pucBuf = NULL;
FILE *pf = NULL;
long lFileSize = ;
time_t tACBegin = {};
double dMemSec = 0.0; pf = fopen(argv[], "rb");
fseek(pf, , SEEK_END);
lFileSize = ftell(pf);
fseek(pf, , SEEK_SET);
pucBuf = (unsigned char *)calloc(lFileSize, );
fread(pucBuf, , lFileSize, pf);
fclose(pf);
for (int i = ; i < ; i ++)
{
std::vector<unsigned char> *pvctPattern = new std::vector<unsigned char>();
int iBegin = rand() % (lFileSize - );
int iLen = rand() % + ;
for (int j = ; j < iLen; j ++)
pvctPattern->push_back(pucBuf[j + iBegin]);
vctPatterns.push_back(pvctPattern);
printf("%x:%u\n", iBegin, iLen);
for (long j = ; j < lFileSize - iLen; j ++)
{
time_t tMemBegin = time(NULL);
if (memcmp(pucBuf + iBegin, pucBuf + j, iLen) == )
printf(">>>>>>>>>>Off:%x\n", j);
dMemSec += difftime(time(NULL), tMemBegin);
}
} InitACGoto(vctPatterns, vctNodes);
ACFail(vctNodes);
tACBegin = time(NULL);
ACSearch(vctNodes[], pucBuf, lFileSize, ACFoundCallBack);
printf("MemTime::%f\nACTime::%f\n", dMemSec, difftime(time(NULL), tACBegin));
ReleaseACNodes(vctNodes);
return;
}

基于AC有限状态机的多模匹配算法的更多相关文章

  1. 基于Qt有限状态机的一种实现方式和完善的人工智能方法

    基于Qt有限状态机的一种实现方式和完善的人工智能方法 人工智能在今年是一个非常火的方向,当然了.不不过今年,它一直火了非常多年,有关人工智能的一些算法层出不穷.人工智能在非常多领域都有应用,就拿我熟悉 ...

  2. java实现多模匹配算法

    这个是好几年前写的了.都统一放到cnblogs上面. --------------------------------Node ---------------------------------- p ...

  3. 基于KMP与Levenshtein模糊匹配算法的银行联行号查询(转)

    在人民银行那里,每个银行的每一个营业网点都有自己唯一的银行联行号,根据这个号码能快速定位一间银行具体的分支行,就像根据一个身份证号码能快速确定一个人一样.例如汇款时,汇款单上要求填写收款人开户行,然后 ...

  4. 基于KMP与Levenshtein模糊匹配算法的银行联行号查询

    在人民银行那里,每个银行的每一个营业网点都有自己唯一的银行联行号,根据这个号码能快速定位一间银行具体的分支行,就像根据一个身份证号码能快速确定一个人一样.例如汇款时,汇款单上要求填写收款人开户行,然后 ...

  5. 基于Unity有限状态机框架

    这个框架是Unity wiki上的框架.网址:http://wiki.unity3d.com/index.php/Finite_State_Machine 这就相当于是“模板”吧,自己写的代码,写啥都 ...

  6. 多模匹配算法之Aho-Corasick

    除剔除那些含有敏感词的文本,由于有大量的敏感词,所以通过简单的正则表达式和字符串查找的方式效率太低,每次都有遍历一次字符串.而AC算法的核心思想就是避免不必要的回溯使搜索一直沿着向前的方向,最大可能的 ...

  7. POJ 3691 DNA repair 基于AC自己主动机DP

    dp[i][j] 它表示的长度 i 下游前缀 j 更改节点的最小数量. 很清楚dp[0][0] = 0; dp[ i ][ j ] = min(dp[ i ][ j ],dp[i-1][k] + (j ...

  8. AC多模式匹配算法

    建议:学习ac算法最好的途径是看论文pdf_Efficient_String_Matching_An_Aid_to_Biblio 一.一般的搜索算法 keyword: { he, she, his, ...

  9. 【工程应用一】 多目标多角度的快速模板匹配算法(基于NCC,效果无限接近Halcon中........)

    愿意写代码的人一般都不太愿意去写文章,因为代码方面的艺术和文字中的美学往往很难兼得,两者都兼得的人通常都已经被西方极乐世界所收罗,我也是只喜欢写代码,让那些字母组成美妙的歌曲,然后自我沉浸在其中自得其 ...

随机推荐

  1. mysql字符类型大小写敏感的讨论

    mysql字符类型默认是不区分大小写的,即select * from t where name='AAA'与='aaa'没区别,以下是测试的例子 (root)); (root,,,,'BbB'); ( ...

  2. 【Ray Tracing The Next Week 超详解】 光线追踪2-5

    Chapter 5:Image Texture Mapping 先看效果: 我们之前的纹理是利用的是撞击点p处的位置信息,比如大理石纹理 而我们今天的图片映射纹理采用2D(u,v)纹理坐标来进行. 在 ...

  3. zip&ftp命令

    zip: C:\Users\IBM_ADMIN>zip -09r Oracle.zip ./Oracle/* C:\Users\IBM_ADMIN>ftp ftp> open adm ...

  4. 潭州课堂25班:Ph201805201 mongo数据 库 第八课 (课堂笔记)

    mongo   进入数据库, exit 退出 show dbs 查数据库 db.createCollection('stu')  创建一个集合, > use binbinswitched to ...

  5. zabbix 添加被监控主机

    点击 configured > host > create host 主机名:输入主机名,允许使用字母数字,空格,点,破折号和下划线 组:从右侧选择框中选择一个或多个组,然后单击 « 将其 ...

  6. 使用Date和SimpleDateFormat类表示时间

    Date类: 使用 Date 类的默认无参构造方法创建出的对象就代表当前时间,我们可以直接输出 Date 对象显示当前的时间,显示的结果如下: Date d = new Date(); System. ...

  7. CentOS 7下设置Docker代理(Linux下Systemd服务的环境变量配置)

    Docker守护程序使用HTTP_PROXY,HTTPS_PROXY以及NO_PROXY环境变量在其启动环境来配置HTTP或HTTPS代理的行为.无法使用daemon.json文件配置这些环境变量. ...

  8. MyEclipse部署WebLogic

    ====================================================================================

  9. spring mvc 异常统一处理

    摘自: http://gaojiewyh.iteye.com/blog/1297746  

  10. 使用JDBC在MySQL数据库中快速批量插入数据

    使用JDBC连接MySQL数据库进行数据插入的时候,特别是大批量数据连续插入(10W+),如何提高效率呢? 在JDBC编程接口中Statement 有两个方法特别值得注意: void addBatch ...