ACM模板 - KMP算法

注释版v1

/*
    时间复杂度:如果文本串的长度为n,模式串的长度为m,那么匹配过程的时间复杂度为O(n),算上计算next的O(m)时间,KMP的整体时间复杂度为O(m + n)。

    算法说明:
            1、先通过目标串(ttr)计算出对应的首尾最长前后缀长度(next[])对应的值
            2、再通过计算出的(next[])“智能”处理目标串在主串中的匹配过程
*/

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

/*
    功能:
        获取 next 数组的值:计算目标串前后缀相同的字符个数

    输入:
         ttr:目标串(下标从0开始)
        next:目标串的最长前缀和最长后缀相同的长度,[标记:长度]=>[-1:0],[0:1],[1:2],[n:n+1]。
              如:"ababaca",长度是7,所以next[0],next[1],next[2],next[3],next[4],next[5],next[6]分别计算的是 a,ab,aba,abab,ababa,ababac,ababaca 的相同的最长前缀和最长后缀的长度。
              由于 a,ab,aba,abab,ababa,ababac,ababaca 的相同的最长前缀和最长后缀是"","","a","ab","aba","","a",所以next数组的值是[-1,-1,0,1,2,-1,0]
        tlen:目标串长度
*/
void Get_Next(char *ttr,int *next,int tlen)
{
    next[0]=-1; // next[0] 初始化为 -1,-1 表示不存在相同的最大前缀和最大后缀
    for(int i=1,j=-1;i<tlen;i++)
    {
        while(j>-1 && ttr[j+1]!=ttr[i]) // 如果下一个不同,那么 j 就变成 next[j],注意 next[j] 是小于 j 的,无论 j 取任何值
            j=next[j]; // 往前回溯
        if(ttr[j+1]==ttr[i]) // 如果相同,j++
            j++;

        next[i]=j; // 这是把算好的 j 值(就是相同的最大前缀和最大后缀长度)赋给 next[i]
    }
}

/*
    功能:
        在主串中匹配目标串;代码和 Get_Next(...) 很类似

    输入:
         str:主串(下标从0开始)
        slen:主串长度
         ttr:目标串(下标从0开始)
        tlen:目标串长度

    输出:
        若存在,返回第一个字符匹配成功的位置(下标从0开始);否则,返回-1
*/
int Index_KMP(char *str,int slen,char *ttr,int tlen)
{
    int *next=new int[tlen];
    Get_Next(ttr,next,tlen);

    for(int i=0,j=-1;i<slen;i++)
    {
        while(j>-1 && ttr[j+1]!=str[i]) // ttr 和 str 不匹配,且 j>-1:表示 ttr 和 str 有部分匹配
            j=next[j]; // 往前回溯
        if(ttr[j+1]==str[i])
            j++;

        if(j==tlen-1) // 说明 j 移动到 ttr 的最末端,匹配完成(成功)
        {
            return i-tlen+1; // 返回相应的位置(首字符匹配的位置,下标从0开始)
        }
    }
    return -1; // 匹配失败
}

/*
    功能:
        目标串在主串中出现的次数(可重叠);代码和 Get_Next(...) 很类似

    输入:
         str:主串(下标从0开始)
        slen:主串长度
         ttr:目标串(下标从0开始)
        tlen:目标串长度

    输出:
        返回出现(匹配成功)的次数
*/
int Count_KMP(char *str,int slen,char *ttr,int tlen)
{
    int cnt=0;
    int *next=new int[tlen];
    Get_Next(ttr,next,tlen);

    for(int i=0,j=-1;i<slen;i++)
    {
        while(j>-1 && ttr[j+1]!=str[i]) // ttr 和 str 不匹配,且 j>-1:表示 ttr 和 str 有部分匹配
            j=next[j]; // 往前回溯
        if(ttr[j+1]==str[i])
            j++;

        if(j==tlen-1) // 说明 j 移动到 ttr 的最末端,匹配完成(成功)
        {
            //j=-1; // 无须此代码,因为通过 next[] 可处理 j 的值
            //i=i-tlen+1; // 无须此代码(主串往前回溯),因为通过 next[] 可处理这过程
            cnt++;
        }
    }
    return cnt; // 返回匹配成功的次数
}

简化版v1

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

void Get_Next(char *ttr,int *next,int tlen)
{
    next[0]=-1;
    for(int i=1,j=-1;i<tlen;i++)
    {
        while(j>-1 && ttr[j+1]!=ttr[i])
            j=next[j];
        if(ttr[j+1]==ttr[i])
            j++;

        next[i]=j;
    }
}

int Index_KMP(char *str,int slen,char *ttr,int tlen)
{
    int *next=new int[tlen];
    Get_Next(ttr,next,tlen);

    for(int i=0,j=-1;i<slen;i++)
    {
        while(j>-1 && ttr[j+1]!=str[i])
            j=next[j];
        if(ttr[j+1]==str[i])
            j++;

        if(j==tlen-1)
            return i-tlen+1;
    }

    return -1;
}

int Count_KMP(char *str,int slen,char *ttr,int tlen)
{
    int cnt=0;
    int *next=new int[tlen];
    Get_Next(ttr,next,tlen);

    for(int i=0,j=-1;i<slen;i++)
    {
        while(j>-1 && ttr[j+1]!=str[i])
            j=next[j];
        if(ttr[j+1]==str[i])
            j++;

        if(j==tlen-1)
            cnt++;
    }

    return cnt;
}

注释版v2

/*
    功能:
        1、【标记 // Index】目标串在主串中首次匹配的索引值
        2、【标记 // Count】目标串在主串中出现的次数(可重叠)

    输入:
         str:主串(下标从0开始)
        slen:主串长度
         ttr:目标串(下标从0开始)
        tlen:目标串长度

    输出:
        1、【标记 // Index】返回首次匹配成功的索引值,否则返回 -1
        2、【标记 // Count】返回出现(匹配成功)的次数

    提示:
        next[i]:
       ----------------------------------------
            i	0	1	2	3	4	5	6	7
        模式串	A	B	C	D	A	B	D	'\0'
       next[i] -1	0	0	0	0	1	2	0
*/
int kmp(char *str,int slen,char *ttr,int tlen)
{
//    int rs = 0; // Count
    int *next=new int[tlen+1]; // +1 是因为为了计算最长模式子串时的真前后缀相同长度
    next[0] =  -1;
    for (int i=0,j=-1;i<tlen;i++) // Get_Next
    {
        while (j!=-1 && ttr[i]!=ttr[j])
            j = next[j];
        next[i+1] = ++j;
    }

    for (int i=0,j=0;i<slen;i++) // Index_KMP or Count_KMP
    {
        while (j!=-1 && str[i]!=ttr[j])
            j = next[j];
        if (++j==tlen)
        {
//            rs++; // Count
            return i-j+1; // Index
        }
    }

//    return rs; // Count
    return -1; // Index
}

简化版v2

int kmp(char *str,int slen,char *ttr,int tlen)
{
//    int rs = 0; // Count
    int *next=new int[tlen+1];
    next[0] =  -1;
    for (int i=0,j=-1;i<tlen;i++)
    {
        while (j!=-1 && ttr[i]!=ttr[j])
            j = next[j];
        next[i+1] = ++j;
    }

    for (int i=0,j=0;i<slen;i++)
    {
        while (j!=-1 && str[i]!=ttr[j])
            j = next[j];
        if (++j==tlen)
        {
//            rs++; // Count
            return i-j+1; // Index
        }
    }

//    return rs; // Count
    return -1; // Index
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ACM 算法模板集 Contents 一. 常用函数与STL 二. 重要公式与定理 1. Fibonacci Number 2. Lucas Number 3. Catalan Number 4. Stirling Number(Second Kind) 5. Bell Number 6. Stirling's Approximation 7. Sum of Reciprocal Approximation 8. Young Tableau 9. 整数划分 10. 错排公式 11. 三角形内切圆半径公式 12. 三角形外接圆半径公式 13. 圆內接四边形面积公式 14. 基础数论公式 三. 大数模板,字符读入 四. 数论算法 1. Greatest Common Divisor最大公约数 2. Prime素数判断 3. Sieve Prime素数筛法 4. Module Inverse模逆元 5. Extended Euclid扩展欧几里德算法 6. Modular Linear Equation模线性方程(同余方程) 7. Chinese Remainder Theorem中国余数定理(互素于非互素) 8. Euler Function欧拉函数 9. Farey总数 9. Farey序列构造 10. Miller_Rabbin素数测试,Pollard_rho因式分解 五. 图论算法 1. 最小生成树(Kruscal算法) 2. 最小生成树(Prim算法) 3. 单源最短路径(Bellman-ford算法) 4. 单源最短路径(Dijkstra算法) 5. 全源最短路径(Folyd算法) 6. 拓扑排序 7. 网络预流和最大流 8. 网络最小费用最大流 9. 网络最大流(高度标号预流推进) 10. 最大团 11. 二分图最大匹配(匈牙利算法) 12. 带权二分图最优匹配(KM算法) 13. 强连通分量(Kosaraju算法) 14. 强连通分量(Gabow算法) 15. 无向图割边割点和双连通分量 16. 最小树形图O(N^3) 17. 最小树形图O(VE) 六. 几何算法 1. 几何模板 2. 球面上两点最短距离 3. 三点求圆心坐标 4. 三角形几个重要的点 七. 专题讨论 1. 树状数组 2. 字典树 3. 后缀树 4. 线段树 5. 并查集 6. 二叉堆 7. 逆序数(归并排序) 8. 树状DP 9. 欧拉路 10. 八数码 11. 高斯消元法 12. 字符匹配(KMP算法) 13. 全排列,全组合 14. 二维线段树 15. 稳定婚姻匹配 16. 后缀数组 17. 左偏树 18. 标准RMQ-ST 19. 度限制最小生成树 20. 最优比率生成树(0/1分数规划) 21. 最小花费置换 22. 区间K大数 23. LCA - RMQ-ST 24. LCA – Tarjan 25. 指数型母函数 26. 指数型母函数(大数据) 27. 单词前缀树(字典树+KMP) 28. FFT(大数乘法) 29. 二分图网络最大流最小割 30. 混合图欧拉回路 31. 无源汇上下界网络流 32. 二分图最小点权覆盖 33. 带约束的轨道计数(Burnside引理) 34. 三分法求函数波峰 35. 单词计数,矩阵乘法 36. 字符和数值hash 37. 滚动队列,前向星表示法 38. 最小点基,最小权点基
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

放羊的牧码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值