• 字符串算法


    HASH

    个人理解:将字符串(...)通过自定义的运算方式转换为数字(...)。这样处理起来更加快捷,也节省内存空间。

    怎样$HASH$?我个人是按数字的进制方法处理的。

    例子一串由小写字母组成的字符串,我们可将其视作一个二十六(或更大)进制的数字,各位上的字母$['a'...'z']$分别对应数字$[0...25]$。

    如$abcdc=0 imes 26^{4}+1 imes 26^{3}+2 imes 26^{2}+3 imes 26^{1}+2 imes 26^{0}$

    KMP

    在文本串($s$)中寻找给定的模式串($s1$)。

    设当前$s1[1...j]$已经与$s[i-j+1...i]$完全匹配。

    这时$s1[j+1]$不等于$s[i+1]$,无法继续匹配。我们要调整模式串$s1$的位置,但我们不暴力地一位位移动。

    我们要预处理一个$nxt$数组。设$nxt[j]=k$,使$s1[1...k]$与$s1[j-k+1...j]$按位相等。如果在$j+1$位失配了,就将$j$指向$nxt[j]$。(显然$nxt[j]$越大越有利)

    //因为 $s1[j-k+1...j]$与 $s[i-k+1...i]$匹配,而 $s1[1...k]$又与 $s1[j-k+1...j]$按位相等,所以易得 $s1[1...k]$与 $s[i-k+1...i]$匹配。

    例子:$s1=$'$ababab$',$nxt[1...6]=[0,0,1,2,3,2]$。

    代码实现 (a为文本串,b为模式串)

    void KMP() {
        int j=0;
        for(int i=1;i<=n;i++) { //n=strlen(a+1)
            while(j&&a[i]!=b[j+1]) j=nxt[j];
            if(a[i]==b[j+1]) j++;
            if(j==m) ans++,j=nxt[j];
        }
    }

    预处理$nxt$数组,就是模式串“自我匹配”的过程啦!(代码和上面很像)

    void GetNXT() {
        nxt[1]=0; 
        int j=0;
        for(int i=2;i<=m;i++) { //m=strlen(b+1)
            while(j&&b[i]!=b[j+1]) j=nxt[j];
            if(b[i]==b[j+1]) j++;
            nxt[i]=j;
        }
    }

    Trie字典树

    我们有六个字符串:'$inn$','$a$','$to$','$tea$','$ted$','$ten$'。将它们建成一棵Trie字典树是这样的:

     

     这个很好理解的,直接放代码(有详细注释)

    struct TrieTree {
        int tot,c[N][Z]; //Z为字符集大小 
        //tot:字典树的节点总数,c[i][j]:i号节点下j字符的节点编号
        bool bo[N]; //bo[i]:i号节点是否为一个字符串的结尾
        void clear() { //字典树的初始化
           tot=0;
           memset(c,0,sizeof(c));
           memset(bo,false,sizeof(bo));
        }
        void insert(char *s) { //在字典树中插入字符串s
           int len=strlen(s),u=0; //u:当前所在的节点编号(现为根节点0)
           for(int i=0;i<len;i++) {
                int v=s[i]-'0'; 
                if(!c[u][v]) c[u][v]=++tot; //没有此节点则新建一个
                u=c[u][v]; //顺着字典树向下走
           } 
           bo[u]=1; //记录u号节点为一个字符串的结尾
        }
        bool search(char *s) { //查询字符串s是否为给定字符串集合中某个串的前缀
            int len=strlen(s),u=0;
            for(int i=0;i<len;i++) {
                int v=s[i]-'0';
                if(!c[u][v]) return 0; //没有此节点,返回false
                u=c[u][v]; //顺着字典树向下走
            }
            return 1; //找到了,返回true
        }
    };

    AC自动机

    前置技能:KMPTrie字典树 (就在上面

    AC自动机的功能还是在给定的文本串中寻找模式串。不同于KMP的是,这次的模式串不止一个。

    看着代码画图理解吧。

    struct Aho_Corasick_Automaton {
        int tot,c[N][26],val[N],fail[N];
        void clear() {
            tot=0;
            memset(c,0,sizeof(c));
            memset(val,0,sizeof(val));
        }
        void insert(char *s) {
            int len=strlen(s),u=0;
            for(int i=0;i<len;i++) {
                int v=s[i]-'a';
                if(!c[u][v]) c[u][v]=++tot;
                u=c[u][v];
            }
            val[u]++;
        }
        void find() {
            for(int i=0;i<26;i++) 
             if(c[0][i]) fail[c[0][i]]=0,q.push(c[0][i]);
            while(!q.empty()) {
                int u=q.front(); q.pop();
                for(int i=0;i<26;i++) 
                 if(c[u][i]) 
                  fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]);
                 else c[u][i]=c[fail[u]][i];
            }
        }
        int query(char *s) {
            int len=strlen(s),u=0,ans=0;
            for(int i=0;i<len;i++) {
                u=c[u][s[i]-'a'];
                for(int v=u;v&&~val[v];v=fail[v]) 
                 ans+=val[v],val[v]=-1;
            }
            return ans;
        }
    };

    完结撒花 //虽然烂尾了

  • 相关阅读:
    MongoDB入门示例及介绍
    Oracle/PLSQL CURSOR FOR Loop
    JAVA写的文件分割与文件合并程序
    synchronized 解决死锁的问题 轉貼
    采用批处理命令对文件进行解压及采用SQLCMD进行数据库挂载
    chapter6作业
    chapter5作业
    chapter4作业
    Linuz系统管理 -----账号管理
    chapter02
  • 原文地址:https://www.cnblogs.com/qq8260573/p/10363292.html
Copyright © 2020-2023  润新知