• KMP算法笔记


    【简述】:

    kmp算法:
    1 kmp是用来匹配字符串,只能够匹配单一的字符串
    2 kmp的算法的过程:
      1:假设文本串的长度为n,模式串的长度为m;
      2:先例用O(m)的时间去预处理next数组,next数组的意思指的是当前的字符串匹配失败后要转到的下一个状态;
      3:利用o(n)的时间去完成匹配;

    3 时间复杂度为o(n+m)即o(n);

    【例题】:

    1. 可重叠匹配:j=nxt[j] 

    求模式串在主串中出现的次数,可重叠【HDU 1686 Oulipo】

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <stack>
    #include <set>
    #include <map>
    #include <string>
    #include <cmath>
    #include <cstdlib>
    #include <deque>
    #include <ctime>
    #define fst first
    #define sec second
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define ms(a,x) memset(a,x,sizeof(a))
    typedef long long LL;
    #define pi pair < int ,int >
    #define MP make_pair
    
    using namespace std;
    const double eps = 1E-8;
    const int dx4[4]={1,0,0,-1};
    const int dy4[4]={0,-1,1,0};
    const int inf = 0x3f3f3f3f;
    const int N=1E4+7;
    string a,b;
    int ans;
    int nxt[N];
    void getnxt( int n)
    {
        int i = 0;
        int j = -1;
        nxt[0] = -1;
        while (i<n)
            if (j==-1||b[i]==b[j]) nxt[++i]=++j;
        else j = nxt[j];
    }
    void kmp( int n,int m)
    {
        int i = 0 ;
        int j = 0 ;
        getnxt(m);
       // for ( int i = 0 ; i < m ; i++) cout<<i<<" "<<nxt[i]<<endl;
        while (i<n)
        {
            if (j==-1||a[i]==b[j]) i++,j++;
            else j = nxt[j];
            if (j==m) ans++,j=nxt[j];
        }
    }
    int main()
    {
    
            ios::sync_with_stdio(false);
            int T;
            cin>>T;
            while (T--)
            {
                cin>>a>>b;
                swap(a,b);
                int la = a.length();
                int lb = b.length();
                ans = 0 ;
                kmp(la,lb);
                cout<<ans<<endl;
            }
    
        return 0;
    }
    可重叠匹配

    2. 不可重叠匹配:j=0

    问模式串在文本串中出现的次数,不允许重叠【HDU 2087 剪花布条】

    //不是j=nxt[j],因为不能重复,只要每次找到的时候j=0一下就好。
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <stack>
    #include <set>
    #include <map>
    #include <string>
    #include <cmath>
    #include <cstdlib>
    #include <deque>
    #include <ctime>
    #define fst first
    #define sec second
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define ms(a,x) memset(a,x,sizeof(a))
    typedef long long LL;
    #define pi pair < int ,int >
    #define MP make_pair
    
    using namespace std;
    const double eps = 1E-8;
    const int dx4[4]={1,0,0,-1};
    const int dy4[4]={0,-1,1,0};
    const int inf = 0x3f3f3f3f;
    const int N=1E4+7;
    string a,b;
    int ans;
    int nxt[N];
    void getnxt( int n)
    {
        int i = 0;
        int j = -1;
        nxt[0] = -1;
        while (i<n)
            if (j==-1||b[i]==b[j]) nxt[++i]=++j;
        else j = nxt[j];
    }
    void kmp( int n,int m)
    {
        int i = 0 ;
        int j = 0 ;
        ans=0;
        getnxt(m);
       // for ( int i = 0 ; i < m ; i++) cout<<i<<" "<<nxt[i]<<endl;
        while (i<n)
        {
            if (j==-1||a[i]==b[j]) i++,j++;
            else j = nxt[j];
            if (j==m) ans++,j=0;
        }
    }
    int main()
    {
    
        ios::sync_with_stdio(false);
        while(cin>>a>>b)
        {
            ans=0;
            if(a[0]=='#'||b[0]=='#')
                break;
           int len1=a.size();
           int len2=b.size();
           kmp(len1,len2);
           printf("%d
    ",ans);
        }
        return 0;
    }
    不重叠匹配

    3. KMP找子串第一次出现的位置 

    #include<stdio.h>
    #include<string.h>
    #define N 1000005
    int next[N],a[N],b[N];
    int m,n;
    void Next()//就是上面的分匹配表的实现
    {
        int i,j;
        i=0;
        j=-1;
        next[i]=j;   //匹配表初值
        while(i<m)
        {
            if(j==-1||b[i]==b[j])
            {
                i++;
                j++;
                next[i]=j;
            }
            else
                j=next[j];
        }
        return ;
    }
    int KMP()//kmp匹配算法
    {
        int i,j;
        i=j=0;
        Next();//先计算部分匹配表
        while(i<n)
        {
            if(j==-1||a[i]==b[j])
            {
                i++;
                j++;
                if(j==m)
                    return i-m+1;//找到目标字符串,返回到主程序。
            }
            else
                j=next[j];//a[i]与b[j]不匹配,查表需要跳过的字符个数。
        }
        return -1;//没有找到返回-1
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            for(int i=0; i<n; i++)
                scanf("%d",&a[i]);
            for(int i=0; i<m; i++)
                scanf("%d",&b[i]);
            printf("%d
    ",KMP());
        }
        return 0;
    }
    第二个数组中的数字在第一个数组中出现的位置

    【题目总结】:

    1.求匹配串在文本串出现的次数

    直接利用第二种写法即可。

    题目:HDU - 1686

    2.求匹配串在文本串第一次匹配成功时的起始位置。

    套用第一种写法即可。

    题目:HDU - 1711 

    3. 给定一个字符串,问我们还需要添加几个字符可以构成一个由n个循环节组成的字符串。 

    利用Next数组的性质,

    1. 假设字符串的长度为len,那么最小的循环节就是cir = len-next[len] ; 
    2. 如果有len%cir == 0,那么这个字符串就是已经是完美的字符串,不用添加任何字符; 
    3. 如果不是完美的那么需要添加的字符数就是cir - (len-(len/cir)*cir)),相当与需要在最后一个循环节上面添加几个。    

    题目:HDU - 3746

    4.给你一个字符串s求出所有满足s[i] == s[i+p] ( 0 < i+p < len )的p ;

    来自于其他人的做法:
    
    •知识点:KMP算法、对next数组的理解
    
    •KMP算法中next数组的含义是什么?
        •next数组:失配指针
        •如果目标串的当前字符i在匹配到模式串的第j个字符时失配,那么我们可以让i试着去匹配next(j)
    •对于模式串str,next数组的意义就是:
        •如果next(j)=t,那么str[1…t]=str[len-t+1…len]
        •我们考虑next(len),令t=next(len);
    •next(len)有什么含义?
    •P [吨1 ...] = P [称为T-1 ...仅]
    •那么,长度为len-next(len)的前缀显然是符合题意的。
    •接下来我们应该去考虑谁?
        •t = next(next(len));
        •t = next(next(next(len)));
    • 一直下去直到t=0,每个符合题意的前缀长是len-t
    题目:FZU - 190 
     
    求出P0···Pi的最大相同前后缀长度k;但是问题在于如何求出这个最大前后缀长度呢?
     
  • 相关阅读:
    css 左右两栏 左边固定右边自适应
    Oracle 执行长SQL
    Oracle 游标基础
    ERROR 000464: Cannot get exclusive schema lock. Either being edited or in use by another application
    MyEclipse 批量更新编码集
    使用Windows服务发布WCF服务【转载】
    JavaScript的陷阱【转载】
    JS图片切换带超链接
    新正则表达式
    asp.net垃圾代码之asp.net去掉垃圾代码,优化aspx页面性能的方法
  • 原文地址:https://www.cnblogs.com/Roni-i/p/9453120.html
Copyright © 2020-2023  润新知