字符串系列——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("\n\tKMP,选择0\n\te-KMP,选择1\n\t请选择:");
    scanf_s("%d", &menu);
    if (menu == 0)
    {
        int x= KMP();
        if (x< 0) printf("\n\t错误\n");
    }
    else if (menu == 1)
    {
        int x= E_KMP();
        if (x< 0) printf("\n\t错误\n");
    }
    else
    {
        printf("\n\t错误\n");
    }
    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\n", 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 ? '\n' : ' ');
    }
    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("\n\tKMP,选择0\n\te-KMP,选择1\n\t请选择:");
    scanf_s("%d", &menu);
    if (menu == 0)
    {
        int x= KMP();
        if (x< 0) printf("\n\t错误\n");
    }
    else if (menu == 1)
    {
        int x= E_KMP();
        if (x< 0) printf("\n\t错误\n");
    }
    else
    {
        printf("\n\t错误\n");
    }
    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\n", 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 ? '\n' : ' ');
    }
    system("pause");
    return 0;
}
KMP与e-KMP【无注释版】

 

 

 

End;

 

转载于:https://www.cnblogs.com/Amaris-diana/p/10654319.html

KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值