参考链接: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. js算法初窥06(算法模式03-函数式编程)

    在解释什么是函数式编程之前,我们先要说下什么是命令式编程,它们都属于编程范式的一种.命令式编程其实就是一块一块的代码,其中包括了我们要执行的逻辑或者判断或者一些运算.也就是按部就班的一步一步完成我们所 ...

  2. Nginx 安装配置文件解析

    源地址:https://www.cnblogs.com/Chiler/p/8027167.html 前言 Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/P ...

  3. Prism中命令可用性无法自动刷新

    http://stackoverflow.com/questions/2444927/wpf-prism-canexecute-method-not-being-called It is most l ...

  4. C++ 继承与派生

    继承的意义? 使程序的设计更符合发展规律,即事物的发展是一个从低级到高级的发展过程,类的继承也是反映由原始的简单代码到丰富的高级代码的过程.它能帮助我们描述事物的层次关系,有效而精确的理解事物,理解事 ...

  5. java.sql.SQLException: Error setting driver on UnpooledDataSource.Caused by: java.lang.IllegalArgumentException: Result Maps collection does not contain value for IStudentDaoMapper.Mapperdao.selectcou

    是因为 Caused by: java.lang.IllegalArgumentException: Result Maps collection does not contain value for ...

  6. git 快照及分支

    分支介绍 分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN. 如果两个平行宇宙互不干扰,那对现在的你也没啥影响.不过,在某个时间点,两个平 ...

  7. IIS远程发布(Web Deploy)

    作为开发人员,我们之前发布应用很可能是拷贝开发环境上发布好的代码文件到应用服务器硬盘中,然后在IIS中部署网站. 但是今天我们讲的是如果直接在我们的开发环境通过VS远程发布网站到应用服务器上,这将极大 ...

  8. hdu4607Park Visit 树的直径

    //给一棵双向树,数中边的权值为1,问对于这颗树中走k个节点的最短路径 //假设k小于这颗数的直径加1,那么走k个节点就没有反复的路,假设大于 //那么大于的节点都须要走两遍 #include< ...

  9. 【T05】套接字接口比XTI_TLI更好用

    1.用于网络编程的API接口有两种: Berkeley套接字 XTL 2.套接字是加州大学伯克利分校为其Unix操作系统版本开发的,TLI是AT&T(贝尔实验室)为Unix系统V3.0开发的 ...

  10. Redis更新的正确方法

    原文(缓存更新的套路):看到好些人在写更新缓存数据代码时,先删除缓存,然后再更新数据库,而后续的操作会把数据再装载的缓存中.然而,这个是逻辑是错误的.试想,两个并发操作,一个是更新操作,另一个是查询操 ...