zoukankan      html  css  js  c++  java
  • Kmp ac自动机

    学习一下ac自动机,多个模式串匹配一串文本,找到文本中模式串匹配的个数

    package main
    
    import (
    	"fmt"
    )
    
    //字典树(前缀树),ac自动机基础
    type Node struct { //字典树
    	next [26]* Node
    	fail *Node //失配指针
    	flag bool
    }
    
    
    //构建字典树
    func build(root *Node ,str string){
    	pre:=root
    	var ptr *Node
    	order:=0 //索引
    	for i:=0;i<len(str);i++{
    		order = int(str[i]-'a')
    		if pre.next[order]==nil{
    			ptr = new(Node)
    			for j:=0;j<26;j++{
    				ptr.next[j] = nil
    			}
    			ptr.flag = false
    			ptr.fail = nil
    			pre.next[order] = ptr  //设置下标
    		}
    		pre = pre.next[order]
    	}
    	pre.flag = true
    }
    
    
    //寻找失配指针,从根开始,把子节点的失配找到
    func disposeFail(root *Node){
    	//层序遍历
    	queue:=make([]*Node,0)
    	var pre,ptr *Node
    	queue = append(queue,root)
    	for len(queue)!=0{
    		pre=queue[0]
    		queue = queue[1:]
    		ptr=nil //每次都初始化
    		for i:=0;i<26;i++{
    			if pre.next[i]!=nil{ //子节点存在
    				if pre==root{  //根则
    					pre.next[i].fail = root
    				}else{
    					ptr = pre.fail
    					for ptr!=nil{  //向上找失配对的指针
    						if ptr.next[i]!=nil{
    							pre.next[i].fail = ptr
    							break
    						}
    						ptr = ptr.fail
    					}
    					if ptr==nil{ //回溯到root,则失配指向root
    						pre.next[i].fail = root
    					}
    				}
    				queue = append(queue,pre.next[i]) //子节点加入队列
    			}
    		}
    	}
    }
    
    
    //具体匹配
    func matchMultiPattern(root *Node,str string)int{
    	order:=0
    	count:=0
    	var pre,ptr *Node
    	pre = root
    	for i:=0;i<len(str);i++{
    		order = int(str[i]-'a')
    		for pre.next[order]==nil&&pre!=root{
    			pre = pre.fail
    		}
    		//找模式串
    		pre = pre.next[order]
    		if pre==nil{  //没找到开启下一轮
    			pre = root
    			continue
    		}
    		ptr = pre  //匹配该点之后沿失败回溯,判断其他点
    		for ptr!=root{
    			if ptr.flag==true{  //到模式串结尾,需要1决定是否增加1
    				count++
    				ptr.flag=false  //计算过,改为false
    			}else{
    				break
    			}
    			ptr = ptr.fail //找失配指针1
    		}
    	}
    	return count
    }
    
    
    
    
    func main(){
    	trim:=new(Node)
    	strs:=[]string{"long","java","life","python","short"}
    	for _,v:=range strs{
    		build(trim,v)
    	}
    	//构建失配
    	disposeFail(trim)
    	fmt.Println(matchMultiPattern(trim,"lifeisshortsoistudypython"))
    }
    

    kmp匹配

    • 经典写法,先求next数组再计算
      next数组为i前面字符前缀后缀最长公共子串的长度,求next较难理解
    next[k]表示p[k]前面有相同前缀尾缀的长度(这个长度是按最大算的),
    p[next[k]]表示相同前缀的最后一个字符,如果p[next[k]]==p[k],
    则可以肯定next[k+1]=next[k]+1,如果p[next[k]]!=p[k],则思考,
    既然next[k]长度的前缀尾缀都不能匹配了,
    是不是应该缩短这个匹配的长度,到底缩短多少呢,
    next[next[k]]表示p[next[k]]前面有相同前缀尾缀的长度(这个长度是按最大算的),
    所以一直递推就可以了,因为这个缩小后的长度都是按最大的算的
    
    • 代码
    // 求next数组,next数组表示 到i为止,前缀和后缀中最长公共字串长度
    func GetNext(str string)[]int{
    	next:=make([]int,len(str))
    	next[0] = -1
    	j:=0
    	k:=-1
    	for j<len(str)-1{ // 后续j++,防止越界
    		if k==-1||str[j]==str[k]{
    			j++
    			k++
    			next[j] = k
    		}else{
    			k = next[k]
    		}
    	}
    	return next
    }
    
    //单个匹配
    func Kmp(str string,ptr string)int{ //返回相匹配到位置下标,只返回第一个
    	i,j:=0,0 //主串和模式串
    	next:=GetNext(ptr) //模式串next数组
    	for i<len(str)&&j<len(ptr){
    		if j==-1||str[i]==ptr[j]{ //不要忘记j==-1的情况
    			i++
    			j++
    		}else{
    			//i 不变,暴力解法时候 i = i-j+1
    			j = next[j]
    		}
    	}
    
    	if j==len(ptr){
    		return i-j     //初始位置
    	}else{
    		return -1      //不匹配
    	}
    }
    
    func main()  {
    	str := "ABABABC"
    	p:="ABA"
    	one:="hello"
    	two:="ll"
    	fmt.Println(Kmp(str,p))  //单个
    
    	fmt.Println(Kmp(one,two))
     }
    
    
  • 相关阅读:
    .Net中通过反射技术的应用插件程序的开发入门
    html超链接button
    WCF 跨域 Http绑定
    在ASP.NET MVC中修改默认代码生成/支架模板
    使用Lambda表达式重构委托
    使用公用表表达式(CTE)简化嵌套SQL
    WCF同域动态绑定服务地址
    WCF 跨域TCP绑定
    Silverlight 基础收集
    WCF服务控制台托管方法(不使用配置文件)
  • 原文地址:https://www.cnblogs.com/9527s/p/15254209.html
Copyright © 2011-2022 走看看