• 【字符串】KnuthMorrisPratt算法


    以下模板代码的字符串下标均从1开始。

    当遇到性能问题时很可能是vector导致的,换成普通数组也可以。

    字符串 (s) 的前缀函数 (pi[i]) 表示长度为 (i) 的前缀中最长的公共前后缀的长度。
    kmp 找出模式字符串 (t) 在文本字符串 (s) 中出现的所有的位置。

    namespace KnuthMorrisPrattAlgorithm {
    
        vi getPi(char *s) {
            int slen = strlen(s + 1);
            vi pi(slen + 1);
            for(int i = 2, j = 0; i <= slen; ++i) {
                for(; j && s[i] != s[j + 1]; j = pi[j]);
                j += (s[i] == s[j + 1]);
                pi[i] = j;
            }
            return pi;
        }
    
        vi kmp(char *s, char *t) {
            int slen = strlen(s + 1), tlen = strlen(t + 1);
            vi pi = getPi(t), res;
            for(int i = 1, j = 0; i <= slen; ++i) {
                for(; j && s[i] != t[j + 1]; j = pi[j]);
                j += (s[i] == t[j + 1]);
                if(j == tlen)
                    res.pb(i - tlen + 1);
            }
            return res;
        }
    
    }
    

    字符串 (s) 的z函数 (z[i]) 表示以 (i) 位置为左端点的后缀中,与字符串 (s) 的最长的公共前缀的长度。
    exkmp:找出从(s[i])开始的,和模式串 (t) 的最长公共前缀。

    namespace ExtendKnuthMorrisPrattAlgorithm {
    
        vi getZ(char *s, int n) {
            vi z(n + 1);
            z[1] = n;
            for(int i = 2, l = 0, r = 0; i <= n; ++i) {
                if(i <= r)
                    z[i] = min(z[i - l + 1], r - i + 1);
                for(; i + z[i] <= n && s[i + z[i]] == s[z[i] + 1]; ++z[i]);
                if(i + z[i] - 1 > r)
                    l = i, r = i + z[i] - 1;
            }
            return z;
        }
    
        vi exkmp(char *s, char *t) {
            int n = strlen(s + 1), m = strlen(t + 1);
            vi z = getZ(t, m), res(n + 1);
            for(int i = 1, l = 0, r = 0; i <= n; ++i) {
                if(i <= r)
                    res[i] = min(z[i - l + 1], r - i + 1);
                for(; i + res[i] <= n && s[i + res[i]] == t[res[i] + 1]; ++res[i]);
                if(i + res[i] - 1 > r)
                    l = i, r = i + res[i] - 1;
            }
            return res;
        }
    
    }
    

    统计既是字符串 (s) 的前缀又是字符串 (s) 的后缀的所有字符串的长度及其出现次数:易知 (z[i]=(n+1-i)) 表示后缀字符串 (s[i,n]) 与字符串 (s) 的最长公共前缀的长度等于后缀字符串 (s[i,n]) 的长度,所以后缀字符串 (s[i,n]) 满足既是字符串 (s) 的前缀又是字符串 (s) 的后缀,且其长度为 (z[i]) 。那么它的出现次数可以直接用dp求出来(长度为 (z[i]+1) 的字符串出现,则长度为 (z[i]) 的字符串也出现)。

    上面这些函数存在越界访问的现象,在对字符串使用时由于字符串末尾一定是特殊字符所以没事,但是对整数数组进行匹配时要小心。

  • 相关阅读:
    正则表达式中的贪婪模式与非贪婪模式详解
    关于Python中正则表达式的反斜杠问题
    每日思考记录(1)
    软件设计——2018年上半年选择题重要知识点
    统一过程UP
    软件设计复习7
    软件设计复习6
    软件设计复习5
    软件设计复习4
    软件设计复习3
  • 原文地址:https://www.cnblogs.com/purinliang/p/14067447.html
Copyright © 2020-2023  润新知