zoukankan      html  css  js  c++  java
  • 算法初探

    更新记录

    【1】2020.06.23-20:13

    • 1.完善KMP内容
    • 2.一点Trie树内容
    • 3.AC自动机(弱化版)思想

    【2】2020.06.24-09:19

    • 1.完善Trie树内容

    【3】2020.06.24-22:47

    • 1.完善AC自动机(弱化版)内容

    目录

    • KMP字符串匹配
    • Trie树(字典树)
    • AC自动机

    正文

    KMP前言

    ABOUT 暴力

    如果你不会:
    return 是新手?"别着急慢慢来,这篇博客可能不适合你":"暴力你再不会就趁早退役吧"

    ABOUT KMP

    KMP它就是普及的坎,过去就过去,过不去等死
    其实没那么严重,前缀树也能打

    翻找了很多关于KMP的博客,发现能让人真正理解这个算法的少之又少
    所以我根据自己的理解来介绍一下这个算法,希望能帮到一些OIer

    朴素算法

    时间复杂度:(O(nm))
    实质就是暴力匹配,这里不过多介绍

    KMP

    为了方便讲述,我们先明确几个概念

    1. G位前(后)缀
      一个字符串的G位前(后)缀就是它前(后)G个字符组成的字符串
    • 例如字符串abgakdsf的3位前缀是abg,五位后缀是akdsf
    1. 最大相同前后缀
      满足以下两个条件:
    • G位前缀与G位后缀相同
    • 在满足上一条的情况下使得G最大
    • 例如字符串abcdbabc的最大相同前后缀是abc

    先举个例子,我们要在abcaabcdabcaabcaabc中查找abcab

    第一次匹配:
    abcaabcdabcaabcaabc
    ||||X
    abcab
    

    普通的来看,这个字符不匹配就应该往后移动一位,然后继续进行匹配

    但是这样完全将有用的信息忽略了

    第一次匹配已经知道了前四个是abca
    此时我们直接将字符串移动某个偏移量

    abcaabcdabcaabcaabc
       |
       abcaa
    

    然后继续进行匹配

    某个偏移量到底是啥呢?

    设主串M,模式T在进行匹配时
    (M[i-j];M[i-j+1]cdots M[i])(T[0]cdots T[j])在进行匹配的时候
    (j-1)位完全匹配,在第(j)位失配
    如果
    (T[0];T[1]cdots T[j-1] e T[1];T[2]cdots T[j])
    那么
    (T[1];T[2]cdots T[j] e M[i-j];M[i-j+1]cdots M[i])
    所以当在某处失配的时候,我们去找这个字符串的最大相同前后缀

    记录G的值,然后从G+1位继续匹配
    这个G就是上文所说的某个偏移量
    这个操作的正确性显然

    在KMP算法中,我们定义一个next数组
    第i位记录前i-1位最大G值
    (当然也有用第i位记录前i位最大G值的,这里笔者习惯用前者)
    那么如何去求next数组每一项的值呢?
    我们用模式串自己匹配自己即可
    即主串为T,模式串也为T

    void getnext(){
    	next[0]=-1;
    	for(int i=0,j=-1;i<=s.length();){
    		if(j==-1||s[i]==s[j])
    			next[++i]=++j;
    		else
    			j=next[j];
    	}
    }
    

    下面放一段运行过程

    abcaabcdabcaabcaabc
    i:0 j:-1
    -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    i:1 j:0
    -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    i:1 j:-1
    -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    i:2 j:0
    -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    i:2 j:-1
    -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    i:3 j:0
    -1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    i:4 j:1
    -1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    i:4 j:0
    -1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
    i:5 j:1
    -1 0 0 0 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0
    i:6 j:2
    -1 0 0 0 1 1 2 3 0 0 0 0 0 0 0 0 0 0 0
    i:7 j:3
    -1 0 0 0 1 1 2 3 0 0 0 0 0 0 0 0 0 0 0
    i:7 j:0
    -1 0 0 0 1 1 2 3 0 0 0 0 0 0 0 0 0 0 0
    i:7 j:-1
    -1 0 0 0 1 1 2 3 0 0 0 0 0 0 0 0 0 0 0
    i:8 j:0
    -1 0 0 0 1 1 2 3 0 1 0 0 0 0 0 0 0 0 0
    i:9 j:1
    -1 0 0 0 1 1 2 3 0 1 2 0 0 0 0 0 0 0 0
    i:10 j:2
    -1 0 0 0 1 1 2 3 0 1 2 3 0 0 0 0 0 0 0
    i:11 j:3
    -1 0 0 0 1 1 2 3 0 1 2 3 4 0 0 0 0 0 0
    i:12 j:4
    -1 0 0 0 1 1 2 3 0 1 2 3 4 5 0 0 0 0 0
    i:13 j:5
    -1 0 0 0 1 1 2 3 0 1 2 3 4 5 6 0 0 0 0
    i:14 j:6
    -1 0 0 0 1 1 2 3 0 1 2 3 4 5 6 7 0 0 0
    i:15 j:7
    -1 0 0 0 1 1 2 3 0 1 2 3 4 5 6 7 0 0 0
    i:15 j:3
    -1 0 0 0 1 1 2 3 0 1 2 3 4 5 6 7 4 0 0
    i:16 j:4
    -1 0 0 0 1 1 2 3 0 1 2 3 4 5 6 7 4 5 0
    i:17 j:5
    -1 0 0 0 1 1 2 3 0 1 2 3 4 5 6 7 4 5 6
    i:18 j:6
    -1 0 0 0 1 1 2 3 0 1 2 3 4 5 6 7 4 5 6
    

    KMP主过程:

    while(l<m.length()){
    		if(r==-1||m[l]==s[r]) l++,r++;
    		else r=next[r];
    		if(r==s.length()){
    			cout<<l-s.length()+1<<"
    ";
    			r=next[r];
    		}
    	}
    

    (想必您已经精通字符串了,快去吊打字典树吧)

    Trie树前言

    就是字典树(前缀树)啊

    实现

    每个节点有N个儿子,一个颜色标记
    N取决于字符的个数,比如我插入的单词中只有小写字母,N开26即可

    Trie树节点的定义:

    int k=1;
    struct Trie{
    	int trie[N];
    	int color;
    }t[10001];
    

    G位前缀相同的字符串,共用一个长度为G的链,在G+1处产生分支

    [ A example ]
    那么插入两个单词inne,innd,询问inne是否在字典树中

    • 插入时循环每一位,如果节点存在就继续访问,不存在就新建,k值自增
    • 询问时循环每一位,如果节点存在就继续访问,不存在就返回0
      []

    然后我们询问单词inn是否在Trie树中

    显然inn在树中,但是我们并没有插入这个单词
    这时候就要靠颜色标记

    对于一个字符串,在其最后一个字符所在的节点上将颜色标记置为1,代表我插入这个单词了
    []

    我们回归刚才的例子,插入单词时e与d的节点的颜色标记标为1,别的不管
    那么经过修改后的询问:

    • 询问时循环每一位,如果节点存在就继续访问,不存在就返回0,如果字符串最后一个字符所在的节点的颜色标记为1返回1,否则返回0

    再次查找inn,虽然能找到,但是我们发现n所对应的节点颜色标记为0

    说明没有这个单词

    Trie树基本思想结束

    插入与查询代码实现:

    void insert(string a){
    	int c,p=0;
    	for(int i=0;i<a.length();i++){
    		c=a[i]-'a';
    		if(!t[p].trie[c])
    			t[p].trie[c]=k++;
    		p=t[p].trie[c];
    	}
    	t[p].color=1;
    }
    bool search(string a){
    	int c,p=0;
    	for(int i=0;i<a.length();i++){
    		c=a[i]-'a';
    		if(!t[p].trie[c])
    			return 0;
    		p=t[p].trie[c];
    	}
    	return t[p].color==1;
    }
    

    AC自动机

    没有前言!

    在Trie树上查找单词时,用KMP思想进行加速

    AC自动机(基本)思想结束

    #include<iostream>
    #include<queue>
    using namespace std;
    #define NUM 26
    struct Trie{
    	int trie[NUM];
    	int e,fail;
    }t[1000010];
    int tot,n;
    string in;
    inline void insert(string s){
    	int c=0,p=0;
    	for(int i=0;i<s.length();i++){
    		c=s[i]-'a';
    		if(!t[p].trie[c])
    			t[p].trie[c]=++tot;
    		p=t[p].trie[c];
    	}
    	t[p].e+=1;
    }
    inline void build(){
    	queue<int>q;int f=0;
    	for(int i=0;i<NUM;i++)
    		if(t[0].trie[i])
    			q.push(t[0].trie[i]);
    	while(q.size()){
    		f=q.front();q.pop();
    		for(int i=0;i<NUM;i++){
    			if(t[f].trie[i]){
    				t[t[f].trie[i]].fail=t[t[f].fail].trie[i];
    				q.push(t[f].trie[i]);
    			}
    			else
    				t[f].trie[i]=t[t[f].fail].trie[i];
    		}
    	}
    }
    inline int query(string s){
    	int c=0,p=0,sum=0;
    	for(int i=0;i<s.length();i++){
    		c=s[i]-'a';
    		p=t[p].trie[c];
    		for(int o=p;o&&t[o].e!=-1;o=t[o].fail){
    			sum+=t[o].e;
    			t[o].e=-1;
    		}
    	}
    	return sum;
    }
    int main(){
    	ios::sync_with_stdio(0);
    	cin>>n;
    	for(int i=0;i<n;i++){
    		cin>>in;
    		insert(in);
    	}
    	build();
    	cin>>in;
    	cout<<query(in);
    }
    

    (您觉得笔者讲的太简单,自己是字符串专家不需要看,那就快去吊打NOI/CTSC/IOI吧)

  • 相关阅读:
    进程与线程
    the art of seo(chapter seven)
    the art of seo(chapter six)
    the art of seo(chapter five)
    the art of seo(chapter four)
    the art of seo(chapter three)
    the art of seo(chapter two)
    the art of seo(chapter one)
    Sentinel Cluster流程分析
    Sentinel Core流程分析
  • 原文地址:https://www.cnblogs.com/zythonc/p/13178110.html
Copyright © 2011-2022 走看看