Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >更快的Aho-Corasick PHP实现

更快的Aho-Corasick PHP实现
EN

Stack Overflow用户
提问于 2012-01-23 18:12:52
回答 4查看 2.2K关注 0票数 6

在PHP中有没有Aho–Corasick的有效实现?在维基百科的文章中提到了一个Aho-Corasick string matching in PHP

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?php

/* 

    This class performs a multiple pattern matching by using the Aho-Corasick algorythm, which scans text and matches all patterns "at once".

    This class can:
    - find if any of the patterns occours inside the text
    - find all occourrences of the patterns inside the text
    - substitute all occourrences of the patterns with a specified string (empty as well)

    Example of usage: 

    $words = array{ "ananas", "antani", "assassin" };
    $pm = new PatternsMatcher();    
    $pm->patterns_array = $words;   
    if ( $pm->multipattern_match( "banananassata" ) ) {
        echo "pattern found!!";         
    }


    This class is definitively open-source under no particular license.  
    If you use it, let me know what you think about it and how to improve it: 

        Marco Nobile (Università degli studi di Milano-Bicocca) - marco.nobile@unimib.it

    The code wasn't deeply tested, use as your own risk.     

    P.S.: in order to use it as a bad words black-lister, I suggest you to wrap the words with two empty spaces (eg.: "ananas"-->" ananas ") 
    in order to avoid sub-patterns detection. Moreover, better delete the word by substituting it with an empty space instead of the empty string.

*/


class PatternsMatcher {

    var $patterns_array;
    var $text;
    var $finals;
    var $delta;
    var $phi;
    var $container; 
    var $M;
    var $ready;


    /* costruttore */
    function PatternsMatcher() {
        $this->finals = array();
        $this->phi = array();
        $this->container = array();
        $this->delta = array();
        $this->patterns_array = array();
        $this->ready = false;
    }


    /* import new pattern */
    function import_pattern( $p ) {
        $this->patterns_array[]=$p;
    }


    /* shortcuts */
    function deltafun( $indice, $carattere ) {
        return $this->delta[$indice][$carattere];
    }       
    function phifun( $indice ) {
        return $this->phi[$indice+1];
    }   


    /*  chiamata (ricorsiva) che controlla l'esistenza di prefissi uguali a questa stringa. 
        il parametro limita il numero di stati oltre il quale non verificare */
    function check_border( $string , $state ) {


        /* se la stringa è lunga 0 non ho trovato prefissi buoni    */
        if ( strlen($string)==0 )  
            return 0;   

        /* se la stringa è più lunga, controlliamo non sia contenuta in un prefisso
            ovvero in una delle stringhe identificate dagli stati precedenti (<state) */
        for ($j=0; $j<$state; $j++) {

            /* se questo suffisso è uguale ad un pattern, ritorna lo stato corrispondente */
            if ( $string == $this->container[ $j ] )
                return $j+1;
        }

        // trovato nulla, riprovo con la sottostringa
        return $this->check_border( substr( $string, 1 ) , $state );                
    }


    /* costruisce la tabella phi (failure) */
    function build_phi( ) {

        /* valore di partenza */
        $this->phi[0]=0;

        /* foreach stato */
        foreach ( $this->container as $index => $string )  {

            /*  controlla se il prefisso di questo pattern ha un suffisso che è... 
                prefisso di un pattern tra quelli identificati dagli stati 0..index */
            $this->phi[ $index ] = $this->check_border( $string , $index );

        }

        return $this->phi;

    }


    /* costruisce la tabella delta (next) */
    function build_delta( ) {

        /* somma delle lunghezze dei patterns */
        $this->M = 0;

        /* ultimo stato */
        $last_state = 0;

        /* contiamo i caratteri dei patterns */
        foreach( $this->patterns_array as $pattern ) {
            $lunghezza = strlen( $pattern );
            $this->M += $lunghezza;
        }

        /* for each pattern... */
        foreach( $this->patterns_array as $key => $pattern ) {

            /* convertiamo le stringhe in array di caratteri  */        
            $string = $pattern;
            $lun = strlen($pattern);

            /* stati iniziali */
            $asf_state = 0;
            $in_pattern_index = 0;

            /* tengo traccia dei prefissi, mi rende la vita più semplice dopo */
            $temp = "";

            /* finché il pattern non è esaurito e la delta è diversa da NIL... */
            while( ($in_pattern_index < $lun) & ( !is_null( $this->deltafun( $asf_state , $string[$in_pattern_index] ) ) ) ) {

                // segui un percorso pre-esistente 
                $asf_state = $this->deltafun( $asf_state , $string[ $in_pattern_index ] );

                // aggiorna il prefisso fin quì
                $temp.=$string[ $in_pattern_index ];

                // cambia carattere del pattern
                $in_pattern_index++;

            }


            /* crea gli eventuali nuovi stati */
            while( $in_pattern_index<$lun ) {

                // salva i prefissi aggiuntivi
                $temp.=$string[ $in_pattern_index ];                
                $this->container[] = $temp;

                // nuovo stato
                $last_state++;

                // salva in delta
                $this->delta[ $asf_state ][ $string[ $in_pattern_index ] ] = $last_state;

                // prossimo carattere (se c'è)
                $in_pattern_index++;
                $asf_state = $last_state;

            }

            /* è uno stato finale! */
            $this->finals[ $asf_state ] = true;

        }

        return $this->delta;

    }


    /* precalcola le tabelle phi e delta; se già calcolate, le ritorna direttamente */
    function generate() {

        /* cache: se abbiamo già precalcolato le tabelle, ritornale direttamente */
        if ($this->ready)   return;

        /* ordina lessicograficamente */
        sort( $this->patterns_array, SORT_STRING );

        /* precalcula le tabelle */         
        $this->build_delta( );      
        $this->build_phi( );

        /* abbiamo precalcolato */
        $this->ready = true;
    }


    /* Aho-Corasick standard */ 
    function multipattern_match( $text ) {

        // generate tables
        $this->generate();

        // necessario per prendere anche le prime occorrenze della frase (es.: pattern = " ab " in "ab ac ").
        $text = " ".$text;

        $i=0;       
        $stato=0;

        while ( $i<strlen($text) ) {

            $n = $this->delta[ $stato ][ $text[$i] ];

            $stato = 
                is_null($n)?    $this->phi[ $stato ] : $n;

            if ( $this->finals[ $stato] ) {
                return $i;
            }

            $i++;
        }       
        return -1;
    }


    /* Aho-Corasick che trova tutte le occorrenze (ritorna un array di tuple {posizione,stringa} ) */
    function multipattern_match_array( $text ) {

        // generate tables
        $this->generate();

        // necessario per prendere anche le prime occorrenze della frase (es.: pattern = " ab " in "ab ac ").
        $text = " ".$text;

        $i=0;       
        $stato=0;
        $result = array();
        $temp = "";

        while ( $i<strlen($text) ) {

            $n = $this->deltafun( $stato , $text[$i] );

            $stato = 
                is_null($n)?    $this->phi[ $stato ] : $n;

            $temp = 
                $stato == 0?
                    "" : $temp.$text[$i];           

            if ( $this->finals[ $stato] ) {
                $result[] = array($temp,$i);
                // echo $temp;
            }           

            $i++;
        }       

        return $result;
    }


    /*  Aho-Corasick modificato per la cancellazione di pattern (blacklist). 
        Il parametro specifica con quale sequenza sostituire lo spazio vuoto */
    function remove_substrings( $text , $with = "" ) {

        // genera le tabelle
        $this->generate();

        // necessario per prendere anche le prime occorrenze della frase (es.: pattern = " ab " in "ab ac ").
        $text = " ".$text;

        // contatore sul T
        $i=0;

        // contatore sul T' (output)
        $j=0;

        // contatore su P
        $k=0;

        // stato sull'ASF
        $stato=0;

        // non ricalcolare la dimensione del testo tutte le volte!
        $luntext = strlen($text);

        // preallochiamo il testo in uscita T' (necessario per le idiosincrasie di PHP)
        $nuovo = str_repeat( " ", $luntext ) ;

        /* finché ci sono caratteri nel testo... */
        while ( $i<$luntext ) {

            // prox stato su ASF
            $n = $this->deltafun( $stato , $text[$i] );

            // è null? usiamo phi
            $stato = 
                is_null($n)?    $this->phifun( $stato ) : $n;

            // aggiorniamo la posizione nella sottostringa (utile per fare backtrack dopo la sostituzione)
            $k = 
                $stato == 0?
                    0 : $k+1;

            // piazza il nuovo carattere
            $nuovo[$j] = $text[$i];

            /* stato di accettazione! cancella all'indietro e riparti */            
            if ( $this->finals[ $stato] ) {

                // backtracking (equivale a cancellare i caratteri)
                $j -= $k;
                $k=0;

                // abbiamo cancellato della roba. dobbiamo riposizionarci sull'ASF!
                $n = $this->deltafun( $stato , substr($with,-1) );

                $stato = 
                    is_null($n)?    $this->phifun( $stato ) : $n;

                // ci posizioniamo sull'ultimo carattere della stringa con cui abbiamo sostituito il pattern
                $i--;
            }   

            // muoviamo i puntatori
            $j++; $i++;         
        }   

        // non ritorniamo l'intera stringa ma solo quella lunga quanto il risultato
        return substr( $nuovo , 0, $j );
    }

}

然而,我很难使用它。它适用于一个婴儿示例,但如果我尝试加载数千个关键字,那么脚本将超过30秒的加载限制。

对于其他脚本语言,有一些很棒的实现,比如用于Perl的http://metacpan.org/pod/Text::Scan和用于Python的http://pypi.python.org/pypi/ahocorasick/0.9。为什么不是PHP呢?

EN

回答 4

Stack Overflow用户

发布于 2012-12-24 03:48:18

Aho-Corasick有一个很棒的C库,请查看项目页面http://sourceforge.net/projects/multifast/?source=dlp

我还需要在PHP中使用Aho Corasick,所以我实现了PHP扩展(.so)作为这个库的包装器。查看我的GitHub:https://github.com/ph4r05/php_aho_corasick

许可证: LGPL。

票数 7
EN

Stack Overflow用户

发布于 2012-01-30 06:21:22

我几乎想否决它,因为已经有了一个实现。您可以将问题命名为“更快的Aho-Corasick PHP实现”之类的标题。

无论如何,PHP真的很慢,这是众所周知的。最好将处理数字的工作留给编译语言和PHP扩展。您可以将此代码重写为扩展,但最好是采用现有的C库并将其包装到扩展中,例如:

http://sourceforge.net/projects/multifast/

如果您正在寻找一种更快的解决方案,那么可以使用shell来包装您推荐的perl / python实现之一。

票数 4
EN

Stack Overflow用户

发布于 2015-04-20 22:51:50

Aho corasick有两个阶段,一个是构建优化树,第二个阶段是使用该树来查找匹配。随着字典中条目数量的增加,第一阶段会变得越来越长。PHP有一个问题,每个请求都是一个无共享的执行,这意味着每个请求都必须重新构建整个二进制。

最简单的解决方案是将algorythm分成两部分,并有一个加载器来构建字典,并将其放入APC (APCU)、memcache或其他快速共享数据存储中。然后使用预加载的字典与另一半执行搜索。

或者,使用一种没有任何共享请求限制的语言创建一个小型REST服务器,这样就可以在全局范围内构建字典,并通过REST使用pwrform服务器。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8976472

复制
相关文章
公众号帖子如何查询
自公众号更新以来,大大小小已经更新了差不多130篇文章了。经常会在公众号的后台收到类似:GEPIA;UCSC XENA的回复。可能这些小朋友是想通过回复来看有没有这些数据库的帖子。但是我们在后台没有设置这些关键词回复的时候,是没办法直接出来帖子的。为此。我们特意来写一篇如何检测相关文章的帖子
医学数据库百科
2021/08/23
1.2K0
公众号帖子如何查询
Reddit 如何实现大规模的帖子浏览计数
本文介绍了Reddit如何实现大规模浏览计数系统,该系统使用基于HyperLogLog的算法来估计用户的浏览量。首先介绍了HyperLogLog算法,然后描述了Reddit是如何利用Redis和Cassandra来实现这个系统的。
企鹅号小编
2018/01/08
1.3K0
Reddit 如何实现大规模的帖子浏览计数
帖子中心,1亿数据,架构如何设计?
帖子中心,是互联网业务中,一类典型的“1对多”业务,即:一个用户能发布多个帖子,一个帖子只有一个发布者。
架构师之路
2020/07/30
1.4K0
帖子中心,1亿数据,架构如何设计?
如何给帖子添加板块角标,Ta来了!
因为我首页是没有板块的,这样的话帖子是什么板块的不点进帖子内就不好分辨。我是站在实用的角度出发,顺便做的好看一些,不是为了花里胡哨所谓的美化。所以不要问我这个怎么美化那个怎么美化,我不会因为我不美化。
用户1287596
2024/10/10
1160
如何屏蔽 Emacs China 论坛指定用户的帖子
Emacs China 作为国内少数中文优质论坛,混的时间久了难免会对某些用户的帖子有些反感,虽然论坛自身有屏蔽的功能[1],但仅仅是把内容用「ignored content」来替代,帖子本身还在,没法直接把帖子直接隐藏掉,因此写了个油猴脚本来做这件事。
飞驰的西瓜
2022/07/26
8880
如何制作 GitHub 个人主页
人们在网上首先发现你的地方是哪里?也许你的社交媒体是人们搜索你时首先发现的东西,亦也许是你为自己创建的投资组合网站。然而,如果你使用GitHub来分享你的代码并参与开源项目,那么你的GitHub个人主页可能是人们为了了解你而去的第一个地方。
chuckQu
2023/09/01
3390
如何制作 GitHub 个人主页
从京东主页里寻找技术的真相
刚刚经过“618购物节”的挑战,京东(此文简称JD)首次向大众公布了618的交易额,那就是1199亿元。什么?才1199亿,阿里系小伙伴肯定在后面偷笑,搞了18天的大促,还不如淘宝“双11”一天的销售额。不过没关系,这次强东哥非常满意,因为“618”这个日子,这个让全国网民们沸腾的节日,已经被京东写进历史,这足以证明强东哥这一年的强势回归是有多么重要。
石瞳禅
2018/09/18
1.3K0
从京东主页里寻找技术的真相
woocommerce如何隐藏SKU
  有时我们不想在woocommerce网站前台显示SKU,如下图所示,因为sku一多整个排版可能会乱,那么要如何隐藏sku呢?随ytkah一起来看看
ytkah
2019/12/20
2.2K0
woocommerce如何隐藏SKU
mac如何删除隐藏文件_如何显示系统隐藏文件
U盘和移动硬盘接入Mac时会产生.Trashes,.Spotlight-V100,.fseventsd等文件
全栈程序员站长
2022/09/20
3.6K0
揭秘-社交帖子新趋势
人类的社交开始于面对面的沟通,后来人们通过写信联络远距离的家人朋友,这就是最好理解的实时互动与异步互动。简单来说,“写信”对比“当面聊天”来说多了一个“等待的时间”,面对面的聊天更加“即时”,二者最本质的差异在于互动与互动间的“时间差”。
腾讯ISUX
2023/08/03
3990
揭秘-社交帖子新趋势
IE浏览器主页被劫持,如何解决主页被篡改问题?
前几天号主的电脑的指纹解锁功能突然不能用了,号主以为是驱动没更新到最新版导致的,去官网下载最新的驱动都安装上了也是不行,后面找Dell客服两个小时也没有找到最终的问题,后面个人怀疑是因为号主研究虚拟化技术导致一些冲突从而指纹识别不能用了,最后一不做二不休直接重置了系统后就恢复了【建议大家把桌面文件属性修改为存到别的盘符,这样就算你重置了系统,桌面的资料都不会丢失。
FreeRonin
2020/09/23
19.9K0
IE浏览器主页被劫持,如何解决主页被篡改问题?
如何使用 Redis 实现大规模的帖子浏览计数
满足上面四个条件,其实比想象中要复杂。为了在实时统计的情况下保持精准度,我们需要知道某一个用户之前是否浏览过一篇文章,所以我们需要为每一篇文章存储浏览过它的用户的集合,并且在每次新增浏览时检查该集合进行去重复操作。
芋道源码
2018/12/29
2.1K0
如何使用 Redis 实现大规模的帖子浏览计数
如何隐藏tableHeaderView或tableFooterView
在项目中,因为同一页面结构体不同,头部是相同的结构,用了两个不同的tableView,头部是统一的view,但是发现tableView.tableHeaderView=_headerView只赋值一次,不支持来回赋值,打印出 NSLog(@"%@",_headerView.superview)仍然是上一个tableview视图。
honey缘木鱼
2019/11/04
1.1K0
怎么找出电脑隐藏的软件(如何清理电脑隐藏软件)
最近,有很多小伙伴想跟我学渗透。平时时间确实太忙了,除了要研发公司项目外,写公号,写博客,录视频,写书稿,维护开源项目,几乎占据了我全部的业余时间。目前确实没有太多的时间教大家,今天,就暂时给大家分享一个小技巧吧,如何彻底隐藏电脑中的“视频”,让你的女朋友再也不能发现你电脑中的小秘密!
全栈程序员站长
2022/08/01
4.6K0
怎么找出电脑隐藏的软件(如何清理电脑隐藏软件)
如何给自己的Github主页来个豪华套餐?
仓库信息 “豪华装修”案例分享 技术大佬型🤖:展示自己的技术栈、开源项目、最近文章 地址:https://github.com/thmsgbrt/thmsgbrt 代码模式👨🏽‍💻:展示自己最近的code内容,code语言和开发时间。 地址:https://github.com/guilyx/guilyx 极简主义✨ 地址:https://github.com/Volcano-Yang/Volcano-Yang 地址:
唐志远
2022/10/27
4480
如何给自己的Github主页来个豪华套餐?
Chome 88如何正确隐藏 webdriver?
在文章最完美方案!模拟浏览器如何正确隐藏特征中,我们提到了使用 CDP 协议执行stealth.min.js文件,从而完美隐藏 Chrome 浏览器的各个特征。
青南
2021/02/02
1.6K0
Chome 88如何正确隐藏 webdriver?
问题帖子--Concurrent Read/Write Map
DK1.5 引入了 concurrent package, 提供了更多的concurrent 控制方法。 还提供了一个 ConcurrentHashMap 类。从API上看,是可以读写同步。多个thread可以同时读取,一个thread写的时候,其他thread都不能读写。 这是一个用处很广、很方便的类。我想,能不能在 jdk1.4 及以下版本也提供一个。于是查看了 ConcurrentHashMap的代码。 我本以为,实现思路应该是用到了 ReadWriteLock. 大致是这样的思路。 Jav
李海彬
2018/03/23
6300
问题帖子--Concurrent Read/Write Map
DK1.5 引入了 concurrent package, 提供了更多的concurrent 控制方法。 还提供了一个 ConcurrentHashMap 类。从API上看,是可以读写同步。多个thread可以同时读取,一个thread写的时候,其他thread都不能读写。 这是一个用处很广、很方便的类。我想,能不能在 jdk1.4 及以下版本也提供一个。于是查看了 ConcurrentHashMap的代码。 我本以为,实现思路应该是用到了 ReadWriteLock. 大致是这样的思路。 Jav
李海彬
2018/03/23
8410
miniblink 的bug收集帖子
应广大网友的热情反馈,只好提前把miniblink 0.0.1 版放出来,果然一堆小问题。 特此开个贴子收集下bug,以及解决情况 1、http://www.zi-han.net/theme/hplus/  网站js为GZIP格式,没有支持。(http://bbs.csdn.net/topics/360104816) 2、滚轮不太灵活。可能是WebLayerImpl::setScrollPositionDouble 收到的太慢。(已解决。设置webWheelEvent.hasPreciseScrolling
龙泉寺扫地僧
2018/06/21
1.3K0
如何使用 Python 隐藏 API 密钥
博客首发:https://bornforthis.cn/posts/19.html
AI悦创
2022/06/21
2.2K0
如何使用 Python 隐藏 API 密钥

相似问题

[:]和[]在python中有什么区别?

10

<>和!=在python中有什么区别吗?

11

with和if在Python2中有什么区别?

28

stdin和sys.argv在python中有什么区别?

10

{%和{%-在小枝中有什么区别?

19
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文