• 字符串系列——KMP模板整理


    KMP模板整理 

    KMP与扩展KMP;

    /*vs 2017/ vs code以外编译器,去掉windows.h头文件和system("pause");*/
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<Windows.h>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int maxn = 100010;
    
    /* KMP */
    void Get_Next(char x[], int m, int Next[])
    {
        /*x[]为模式串P, m为模式串P的长度*/
        int i = 0, j;
        j = Next[0] = -1;
        /*以P[0]为终点的子串没有真前缀,有且只有一个假前缀为本身,故Next[0]= -1*/
        while (i < m)
        {
            while (-1 != j && x[i] != x[j]) j = Next[j];
            /*while当P[i]与P[j]不匹配时,
            去寻找上一个与P[j]处境相同的位置,即有相同前缀的点Next[j],与P[i]比较*/
            Next[++i] = ++j;
            /*当P[i]与P[j]处境相似且元素相等时,
            那么Next[i+ 1]= j+ 1; 意思是P[i+ 1]与P[j+ 1]的处境相似,即有相同的前缀*/
        }
    }
    int KMP_Count(char x[], int m, char y[], int n, int Next[])
    {
        /*x[]为模式串P,m为P的长度;y[]为文本串T,n为T的长度*/
        int i, j;
        i = j = 0;
        int cnt = 0;
        while (i < n)
        {
            while (-1 != j && y[i] != x[j]) j = Next[j];
            /*若T[i]!= P[j];则寻找上一个与P[j]前缀相同的地址Next[j];然后与T[i]比较*/
            ++i; ++j;
            if (j >= m)
            {
                ++cnt;
                j = Next[j];
            }
        }
        return cnt;
    }
    
    /* 扩展KMP */
    void Get_e_Next(char x[], int m, int e_Next[])
    {
        /*x[]为模式串P;m是P的长度;e_Next[i]表示模式串P中,
        以P[i]为起点的后缀与模式串P的最长公共前缀的长度*/
        e_Next[0] = m;
        /*字符串s0与自身的最长前缀长度等于自身的长度*/
        int j = 0;
        while (j + 1 < m&& x[j] == x[j + 1]) j++;
        e_Next[1] = j;
        /*用while循环寻找以P[1]为起点的后缀与P的最长公共前缀*/
        int k = 1;
        for (int i = 2; i < m; i++)
        {
            /*此时,以P[k]为起点的后缀是最新(近)一次循环查找过与模式串P最长公共前缀的子串*/
            int p = k+ e_Next[k] - 1;
            /*;P[p]是模式串P以P[k]为起点的后缀与P的最大公共前缀的末位元素;
            即:P[k, p]= P[0, p- k]; 最大公共前缀的长度为(p- k+ 1)*/
            int L = e_Next[i - k];
            /*由上面P[k, p]= P[0, p- k]可得,故P[i, p]= P[i- k, p- k]; 
            L= e_Next[i- k]; 即以P[i- k]为起点的后缀有长度为L的前缀与P[0, L- 1]相同,
            即由上面的关系可证,P[i]为起点的后缀也与P有一个长度为L的最大公共前缀*/
            if (i + L <= p)
            {
                /*如果i+ L的长度不超过目前已扫描的范围,则e_Next[i]= L;
                否则,则要重新扩大处理的范围*/
                e_Next[i] = L;
            }
            else
            {
                /*若上面判断目前扫描的范围已经不足时,继承之前处理的结果,继续向后处理*/
                j = max(0, p - i + 1);
                /*j为p- i+ 1,即P[i]之后已经处理过的元素数量;P[i+ j]是其后第一个未处理的元素*/
                while (i + j < m&& x[i + j] == x[j]) j++;
                /*从P[i+ j]开始,比较P[i]为起点的后缀与P,在两者第j位以后的部分开始比较*/
                e_Next[i] = j;
                k = i;
                /*处理完成后,k更新为i;i+ e_Next[i]- 1是当前处理过的最后一个元素;
                当以后的处理要超过这一界线时,要再次处理,然后更新k的值*/
            }
        }
    }
    void e_KMP(char x[], int m, char y[], int n, int e_Next[], int extend[])
    {
        /*x[]是模式串P,m是P的长度,y[]是文本串T,n是T的长度*/
        int j = 0;
        while (j < n&& j < m&& x[j] == x[j]) j++;
        extend[0] = j;
        /*用while循环寻找以T[0]为起点的后缀与P的最长公共前缀*/
        int k = 0;
        for (int i = 1; i < n; i++)
        {
            int p = extend[k] + k - 1;
            int L = e_Next[i - k];
            if (i + L < p + 1) 
            {
                extend[i] = L; 
            }
            else
            {
                j = max(0, p - i + 1);
                while (i + j < n&& j < m&& y[i + j] == x[j]) j++;
                extend[i] = j;
                k = i;
            }
        }
    }
    char ss_0[maxn];
    char ss_1[maxn];
    int Next[maxn];
    int e_Next[maxn];
    int extend[maxn];
    int KMP();
    int E_KMP();
    int main()
    {
        int menu;
        printf("
    	KMP,选择0
    	e-KMP,选择1
    	请选择:");
        scanf_s("%d", &menu);
        if (menu == 0)
        {
            int x= KMP();
            if (x< 0) printf("
    	错误
    ");
        }
        else if (menu == 1)
        {
            int x= E_KMP();
            if (x< 0) printf("
    	错误
    ");
        }
        else
        {
            printf("
    	错误
    ");
        }
        system("pause");
        return 0;
    }
    int KMP()
    {
        /*可直接修改函数名,为KMP的主函数*/
        system("cls");
        cin >> ss_0;
        cin >> ss_1;
        int len_0 = strlen(ss_0);
        int len_1 = strlen(ss_1);
        Get_Next(ss_0, len_0, Next);
        int tmp= KMP_Count(ss_0, len_0, ss_1, len_1, Next);
        printf("%d
    ", tmp);
        return 0;
    }
    int E_KMP()
    {
        /*可直接修改函数名,为扩展KMP的主函数*/
        system("cls");
        cin >> ss_0;
        cin >> ss_1;
        int len_0 = strlen(ss_0);
        int len_1 = strlen(ss_1);
        Get_e_Next(ss_0, len_0, e_Next);
        e_KMP(ss_0, len_0, ss_1, len_1, e_Next, extend);
        for (int i = 0; i < len_1; i++)
        {
            printf("%d%c", extend[i], i == len_1 - 1 ? '
    ' : ' ');
        }
        system("pause");
        return 0;
    }
    KMP与e-KMP【带注释版】
    /*vs 2017/ vs code以外编译器,去掉windows.h头文件和system("pause");*/
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<Windows.h>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int maxn = 100010;
    
    /* KMP */
    void Get_Next(char x[], int m, int Next[])
    {
        /*x[]为模式串P, m为模式串P的长度*/
        int i = 0, j;
        j = Next[0] = -1;
        while (i < m)
        {
            while (-1 != j && x[i] != x[j]) j = Next[j];
            Next[++i] = ++j;
        }
    }
    int KMP_Count(char x[], int m, char y[], int n, int Next[])
    {
        /*x[]为模式串P,m为P的长度;y[]为文本串T,n为T的长度*/
        int i, j;
        i = j = 0;
        int cnt = 0;
        while (i < n)
        {
            while (-1 != j && y[i] != x[j]) j = Next[j];
            ++i; ++j;
            if (j >= m)
            {
                ++cnt;
                j = Next[j];
            }
        }
        return cnt;
    }
    
    /* 扩展KMP */
    void Get_e_Next(char x[], int m, int e_Next[])
    {
        /*x[]为模式串P;m是P的长度;e_Next[i]表示模式串P中,
        以P[i]为起点的后缀与模式串P的最长公共前缀的长度*/
        e_Next[0] = m;
        int j = 0;
        while (j + 1 < m&& x[j] == x[j + 1]) j++;
        e_Next[1] = j;
        int k = 1;
        for (int i = 2; i < m; i++)
        {
            int p = k+ e_Next[k] - 1;
            int L = e_Next[i - k];
            if (i + L <= p)
            {
                e_Next[i] = L;
            }
            else
            {
                j = max(0, p - i + 1);
                while (i + j < m&& x[i + j] == x[j]) j++;
                e_Next[i] = j;
                k = i;
            }
        }
    }
    void e_KMP(char x[], int m, char y[], int n, int e_Next[], int extend[])
    {
        /*x[]是模式串P,m是P的长度,y[]是文本串T,n是T的长度*/
        int j = 0;
        while (j < n&& j < m&& x[j] == x[j]) j++;
        extend[0] = j;
        int k = 0;
        for (int i = 1; i < n; i++)
        {
            int p = extend[k] + k - 1;
            int L = e_Next[i - k];
            if (i + L < p + 1) 
            {
                extend[i] = L; 
            }
            else
            {
                j = max(0, p - i + 1);
                while (i + j < n&& j < m&& y[i + j] == x[j]) j++;
                extend[i] = j;
                k = i;
            }
        }
    }
    char ss_0[maxn];
    char ss_1[maxn];
    int Next[maxn];
    int e_Next[maxn];
    int extend[maxn];
    int KMP();
    int E_KMP();
    int main()
    {
        int menu;
        printf("
    	KMP,选择0
    	e-KMP,选择1
    	请选择:");
        scanf_s("%d", &menu);
        if (menu == 0)
        {
            int x= KMP();
            if (x< 0) printf("
    	错误
    ");
        }
        else if (menu == 1)
        {
            int x= E_KMP();
            if (x< 0) printf("
    	错误
    ");
        }
        else
        {
            printf("
    	错误
    ");
        }
        system("pause");
        return 0;
    }
    int KMP()
    {
        /*可直接修改函数名,为KMP的主函数*/
        system("cls");
        cin >> ss_0;
        cin >> ss_1;
        int len_0 = strlen(ss_0);
        int len_1 = strlen(ss_1);
        Get_Next(ss_0, len_0, Next);
        int tmp= KMP_Count(ss_0, len_0, ss_1, len_1, Next);
        printf("%d
    ", tmp);
        return 0;
    }
    int E_KMP()
    {
        /*可直接修改函数名,为扩展KMP的主函数*/
        system("cls");
        cin >> ss_0;
        cin >> ss_1;
        int len_0 = strlen(ss_0);
        int len_1 = strlen(ss_1);
        Get_e_Next(ss_0, len_0, e_Next);
        e_KMP(ss_0, len_0, ss_1, len_1, e_Next, extend);
        for (int i = 0; i < len_1; i++)
        {
            printf("%d%c", extend[i], i == len_1 - 1 ? '
    ' : ' ');
        }
        system("pause");
        return 0;
    }
    KMP与e-KMP【无注释版】

    End;

  • 相关阅读:
    第一章 Shell基础知识
    keepalived与LVS实现高可用
    集群简介
    基于NFS v4版本搭建NFS服务器
    LDAP安装步骤
    Nginx配置阿里云https服务
    zabbix基础
    Apache、Nginx和Tomcat之虚拟主机配置
    标准盒模型和怪异盒模型的区别
    js中数组扁平化处理
  • 原文地址:https://www.cnblogs.com/Amaris-diana/p/10654319.html
Copyright © 2020-2023  润新知