字符串匹配

痴心易碎 提交于 2019-11-26 19:45:23

字符串匹配

BF算法(朴素模式匹配)

时间复杂度O(m*n),普通的模式匹配算法
BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;
若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

模板

输出s2在s1里出现的次数
我写的,两行获取两行字符,第二行的字符串在第一行的字符串匹配,看看有几个

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e6+5;
int main(){
    char str[maxn];
    gets(str);

    char s[maxn];
    gets(s);

    int len1=strlen(str);
    int len2=strlen(s);

    int cnt=0;
    for(int i=0;i<len1;i++){
        if(str[i]==s[0]){
            int flag=1;
            for(int j=0;j<len2;j++){
                if(str[i+j]==s[j]){
                }else{
                    flag=0;
                    break;
                }
            }

            if(flag)cnt++;
        }
    }

    printf("%d\n",cnt);

    return 0;
}

匹配成功,继续,匹配失败回溯

输出第一次匹配的下标

#include <iostream>
#include <string.h>
using namespace std;
int BF(char a[],char b[]){
    int index=0;//第一个字符串的下标
    int i=0;//第一个字符串目前下标
    int j=0;//第二个字符串目前下标
    int len1=strlen(a);
    int len2=strlen(b);
    while(i!=len1&&j!=len2){
        if(a[i]==b[j]){
            i++;
            j++;
        }else{
            index++;
            i=index;
            j=0;
        }
    }
    if(j==len2) return index+1;//匹配成功
    else return 0;
}
int main(){
    char a[200];
    cout<<"请输入主串:";
    cin>>a;
    char b[200];
    cout<<"请输入子串:";
    cin>>b;
    cout<<"串在主串首次匹配的位置是:"<<BF(a,b)<<endl;
    return 0;
}

kmp算法---部分匹配表(Partial Match Table)

时间复杂度O(m+n)

前缀:指的是字符串的子串中从原串最前面开始的子串,如abcdef的前缀有:a,ab,abc,abcd,abcde
后缀:指的是字符串的子串中在原串结尾处结尾的子串,如abcdef的后缀有:f,ef,def,cdef,bcdef

kmp算法最大的特点是加入了next[i]数组
next[i](i从1开始算)代表着,除去第i个数,在一个字符串里面从第一个数到第(i-1)字符串前缀与后缀最长重复的个数。

在“aba”中,前缀是“ab”,后缀是“ba”,那么两者最长的子串就是“a”;
在“ababa”中,前缀是“abab”,后缀是“baba”,二者最长重复子串是“aba”;
在“abcabcdabc”中,前缀是“abcabcdab”,后缀是“bcabcdabc”,二者最长重复的子串是“abc”;

next[1] = -1,代表着除了第一个元素,之前前缀后缀最长的重复子串,这里是空 ,即"",没有,我们记为-1,代表空。(0代表1位相同,1代表两位相同,依次累加)。
next[2] = -1,即“a”,没有前缀与后缀,故最长重复的子串是空,值为-1;
next[3] = -1,即“ab”,前缀是“a”,后缀是“b”,最长重复的子串“”;
next[4] = 1,即"aba",前缀是“ab”,后缀是“ba”,最长重复的子串“a”;next数组里面就是最长重复子串字符串的个数
next[5] = 2,即"abab",前缀是“aba”,后缀是“bab”,最长重复的子串“ab”;
next[6] = 3,即"ababa",前缀是“abab”,后缀是“baba”,最长重复的子串“aba”;
next[7] = 1,即"ababaa",前缀是“ababa”,后缀是“babaa”,最长重复的子串“a”;
next[8] = 1,即"ababaaa",前缀是“ababaa”,后缀是“babaaa”,最长重复的子串“a”;
next[9] = 2,即"ababaaab",前缀是“ababaaa”,后缀是“babaaab”,最长重复的子串“ab”;
next[10] = 3,即"ababaaaba",前缀是“ababaaab”,后缀是“babaaaba”,最长重复的子串“aba”;
next[11] = 4,即"ababaaabab",前缀是“ababaaaba”,后缀是“babaaabab”,最长重复的子串“abab”;
next[12] = 5,即"ababaaababa",前缀是“ababaaabab”,后缀是“babaaaababa”,最长重复的子串“ababa”;

首先开始计算主串与子串的字符,设置主串用i来表示,子串用j来表示,如果ptr[i]与a[i]相等,那么i与j就都加1:

模板

https://www.luogu.org/problem/P3375
输出匹配的首字母下标和next[]数组

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
char a[1000005];
char b[1000005];
int next[1000005];
int len1,len2;
void getnext(){
    int p=0;//p是下标
    next[1]=0;
    for(int i=2;i<=len2;i++){
        while(p&&b[i]!=b[p+1]){
            p=next[p];
        }
        if(b[p+1]==b[i])p++;
        next[i]=p;
    }
}
void kmp(){
    int p=0;
    for(int i=1;i<=len1;i++){
        while(p&&b[p+1]!=a[i])p=next[p];
        if(b[p+1]==a[i])p++;
        if(p==len2){
            printf("%d\n",i-len2+1);
            p=next[p];//改成p=0的话是不重复
        }
    }
    for(int i=1;i<=len2;i++)printf("%d ",next[i]);

}

int main(){


    scanf("%s",a+1);
    scanf("%s",b+1);
    len1=strlen(a+1);
    len2=strlen(b+1);
    getnext();
    kmp();

    return 0;
}

例题

HDU2087剪花布条--要求匹配不重复

http://acm.hdu.edu.cn/showproblem.php?pid=2087

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
char a[1000005];
char b[1000005];
int Next[1000005];
int len1,len2;
int cnt;
void getnext(){
    int p=0;
    Next[1]=0;
    for(int i=2;i<=len2;i++){
        while(p&&b[i]!=b[p+1]){
            p=Next[p];
        }
        if(b[p+1]==b[i])p++;
        Next[i]=p;
    }
}
void kmp(){
    int p=0;
    for(int i=1;i<=len1;i++){
        while(p&&b[p+1]!=a[i])p=Next[p];
        if(b[p+1]==a[i])p++;
        if(p==len2){
            cnt++;
            p=0;//达到不重复的目的
        }
    }

}

int main(){
    while(1){
        cnt=0;
        scanf("%s",a+1);
        if(strcmp(a+1,"#")==0)break;
        scanf("%s",b+1);
        len1=strlen(a+1);
        len2=strlen(b+1);
        getnext();
        kmp();

        printf("%d\n",cnt);
    }
    return 0;
}

HDU1711Number Sequence---输出匹配的第一个位置

http://acm.hdu.edu.cn/showproblem.php?pid=1711

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int a[1000005];
int b[1000005];
int Next[1000005];
int len1,len2;
int cnt;
int flag=-1;
void getnext(){
    int p=0;
    Next[1]=0;
    for(int i=2;i<=len2;i++){
        while(p&&b[i]!=b[p+1]){
            p=Next[p];
        }
        if(b[p+1]==b[i])p++;
        Next[i]=p;
    }
}
void kmp(){
    int p=0;
    for(int i=1;i<=len1;i++){
        while(p&&b[p+1]!=a[i])p=Next[p];
        if(b[p+1]==a[i])p++;
        if(p==len2){
            flag=i-len2+1;
            p=Next[p];//
            return;
        }
    }

}

int main(){

    int t;
    cin>>t;
    while(t--){
        flag=-1;
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        for(int j=1;j<=m;j++){
            scanf("%d",&b[j]);
        }

        len1=n;
        len2=m;

        getnext();
        kmp();
        printf("%d\n",flag==-1?-1:flag);

    }
    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!