• KMP回顾学习


    记住这张图,getnext就是对一个已知的待匹配的串进行分析,nex【i】表示当a【i】匹配失败后我能跳到哪里,继续尝试匹配,而不是每一次失败都从头再来,先来看看代码

    const int maxn = 1e5;
    int net[maxn];
    char a[maxn];
    void get_next(int len)
    {
        int i = 0,j = -1;
        nex[0] = -1;//别忘记初始化,和汽车的发动机一样重要
        while(i < len)
        {
            if(j == -1 || a[i] == a[j])
            {
                net[++i] = ++j;
            }
            else
            {
                j = net[j];
            }
        }
    }
    

    j = -1是一个特殊判断代表到了头是在不能尝试匹配了得从头开始了
    而a[i] = a[j]的时候,就可已更新i + 1位的net数组,他能跳的位置也就是j + 1
    当a[i] != a[j]的时候,我们也是寻找的它能跳的最大(远)的点,所以先跳到net[j]去尝试匹配,……直到j=-1不能配为止,那么net[i+1]=0,也只能从头开始了

    ———————————————————————————————————————————————

    看HDU3336

    让你求每一个前缀的出现次数,是对next数组意义的考察

    看那个图:是在j位置失配时能跳到的最远的位置,前提是p0 --pk-1和pj-k--pj-1相同(匹配)才能跳到k-1+1的位置再次进行匹配,也就是每一个>0的next【i】都代表0——i-1的字符串内有匹配项,我们要考虑的是断开的位置,是next数组断开的位置,如果next[i] + 1 == next[i+1]的化,代表匹配项还没结束,还在一直匹配着,我们得到结束点,然后计算前缀重复出现的次数

    注意这个题是不允许交叉计算的例如ababa中aba止出现了一次,所以对我们找到的部分最长匹配串,我们是不用考虑其内部还有子匹配串的情况的 ,因为会交叉
    例如a  b  a  b  a  b

      -1 0  0  1  2  3  4

    面对一整个串我们只能找到的是abab这个部分最长匹配串对应的next值是4,最后结果加的4对应的分别是a,ab,(ab)a,(ab)ab,这是存在交叉

    时候的运算

    不存在交叉的时候最好考虑了……

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    #include <algorithm>
    using namespace std;
    const int maxn = 200005;
    
    int net[maxn];
    char a[maxn];
    
    void get_next(int len)    //KMP原始next值
    {
        int i = 0, j = -1;
        net[0] = -1;
        while (i < len)
        {
            if (j == -1 || a[i] == a[j])
            {
                net[++i] = ++j;
            }
            else j = net[j];
        }
    }
    int main()
    {
        int t, res,len;
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d",&len);
    
            scanf("%s",&a);
            get_next(len);
            res = net[len] + len;  
            for (int i = 0; i < len ; i++)
                if (net[i] > 0 && net[i] + 1 != net[i+1])
                    res += net[i];     
    printf ("%d ", res%10007); } return 0; }

     这个题不大好~~就这样草草结束吧
    ——————————————————————————————————————————————————

    再来看一道2087这是在kmp这个题比较入门,正常的匹配过程中改变一下条件就好了4

    int kmp(int len1,int len2)
    {
        int i = 0,j = 0,cnt = 0;
        while(i < len1)
        {
            if(j == -1 || a[i] == b[j])
            {
                i++;
                j++;
            }
            else
            {
                j = net[j];
            }
            if(j == len2)
            {
                j = 0;
                cnt++;
            }
        }
        return cnt;
    }
    

     然后HDU1867

    考的是字符串相加,一个串的前缀和另一个串的后缀可以结合,优先输出结合后长度最小的,如果长度都相等,救输出字典序最小的

     一开始我做的相当的麻烦,没有一点模块化编程的想法

    实现模块化就是用一个函数实现a + b 和b + a 的结果

    在这里用到了指针!!
    在kmp中我们求net(因为net不像是原来的题目一样求一次就过了,他需要求b再求a)

    然后是改编版的kmp:因为我们寻求的是前缀和后缀相同,所以为了避免完全包含的情况入abcbc 和 bc的出现我们while的条件是i<l1:也就是母串必须遍历到头,且注意对j 重新更新为0的判断(当j == l2 && i != l1)的时候才进行

    然后返回j的位置,就是,匹配后我该输出的另一个的骑士位置

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdio>
    #define INF 0x3f3f3f3f
    #define maxn 100000+10
    using namespace std;
    
    char str1[maxn], str2[maxn];
    int net[maxn];
    
    void getnet(char *x,int len)
    {
        int i = 0,j = -1;
        net[0] = -1;
        while(i < len)
        {
            if(j == -1 || x[i] == x[j])
            {
                net[++i] = ++j;
            }
            else j =net[j];
        }
    }
    int kmp(char *s1,char *s2)
    {
        int i = 0,j = 0;
        int l1 = strlen(s1),l2 = strlen(s2);
        getnet(s2,l2);
        while( i < l1)
        {
            if(j == -1 || s1[i] == s2[j])i++,j++;
            else j =net[j];
            if(j == l2 && i != l1)() j = 0;
        }
        return j;
    }
    int main()
    {
        while(~scanf("%s",str1))
        {
            scanf("%s",str2);
            int tem1 = kmp(str1,str2);
            int tem2 = kmp(str2,str1);
            if(tem1 > tem2)
            {
                printf("%s",str1);
                printf("%s
    ",&str2[tem1]);
            }
            else if(tem1 < tem2)
            {
                printf("%s",str2);
                printf("%s
    ",&str1[tem2]);
            }
            else
            {
                if(strcmp(str1,str2) < 0)printf("%s%s
    ",str1,&str2[tem1]);
                else printf("%s%s
    ",str2,&str1[tem2]);
            }
        }
        return 0 ;
    }
    
  • 相关阅读:
    小程序官方请求封装
    小程序天/小时/分秒倒计时封装
    小程序不定数量左右滑动中间放大轮播图效果
    小程序换行符检测换行
    小程序点击图片重新排序写法
    基于webuploader.js的单图片上传封装
    VMware Fusion 11 序列号
    Ionic 4 beta + Capacitor beta 尝鲜
    C语言学习笔记之动态分配数组空间
    C语言学习笔记之获取文件长度
  • 原文地址:https://www.cnblogs.com/DF-yimeng/p/8491011.html
Copyright © 2020-2023  润新知