字符串匹配

来源:互联网 发布:oppo应用分身软件 编辑:程序博客网 时间:2024/06/05 13:47

1.暴力匹配(c++stl)
很方便不是么

2.KMP
经历wa了30多次我终于

决定抄题解了

最后又wa了二十多次,手玩了20分钟

终于自己写过了

首先我们应该明白next数组时表示一个前【】个字符的公共前缀
求解next数组时

采用递推

因为如果一个数要能与前面继续匹配就是和前一个数的前缀继续匹配如果匹配上了
最长公共前缀的长度就是这个匹配上的字符的位置(+1,-1的个人写法问题如长度1但是匹配的字符是第0个)

如果匹配不上那么如果它能匹配那么它一定要匹配这个数前一个数的前缀的前缀的后一位(因为无法匹配前缀且前缀的前缀一定和这个数前面的数匹配)

那么只需要写一个while就可以递推

注意边界注意边界注意边界

kmp的思想就是找前后缀来在匹配是跳过一些不需要的位置

next数组是kmp算法的精华所在
(很多时候用kmp只是利用他的next数组)

上代码

#include<bits/stdc++.h>using namespace std;int nex[1005];char s[1005];char t[1000006];int main(){    //freopen("in.in","r",stdin);    //freopen("out1.out","w",stdout);    scanf("%s%s",&t,&s);    int lens=strlen(s);    for(int i=1;i<lens;i++){        int j=i-1;        while(nex[j]!=0&&s[nex[j]]!=s[i]){            j=nex[j]-1;        }        if(s[i]==s[nex[j]]) nex[i]=nex[j]+1;    }    int lent=strlen(t);    for(int i=0;i<lent-lens+1;i++){        if(s[0]==t[i]){            int j=1;            while(j<lens&&s[j]==t[i+j]){                j++;            }            if(j==lens) printf("%d\n",i+1);            i+=nex[j];        }    }    for(int i=0;i<lens;i++){        printf("%d ",nex[i]);    }    return 0;}

ps:错了太多次被洛谷认为恶意卡评测全TLE
以后出现这种情况多等上一会就好了

3.sunday
据说kmp在匹配随机串的时候的效果不比stl强多少只是有人专门卡而已
此算法在匹配随机串时速度远超kmp。
注意此算法的复杂度不稳定可能被卡
而KMP的复杂度是严格的O(n)
有 兴趣的可以自行百度“sunday算法”
或参考洛古kmp的题解

ps:洛谷蜜汁KMP TLE(可能是我太弱了吧)
很多人都写sunday
KMP很重要很重要很重要
next数组的用法远超匹配本身的重要性

include<iostream>#include<cstring>#include<cstdlib>#include<string>using namespace std;int nxt[256]={0},l,ll,pre[1001];char a[1000001],b[1001];void fai(int n) {    pre[0]=-1;    for(int i=1,k=-1;i<n;pre[i++]=k)    {        while(k>=0&&b[i]!=b[k+1])            k=pre[k];        if(b[i]==b[k+1])            k++;    }}void Sunday(){    for(int i=0;i<=255;i++)        // 失配指针初始化       nxt[i]=ll+1;    for(int i=0;i<ll;i++)           //建失配指针      nxt[b[i]]=ll-i;    int i=0,j=0;    while((j<ll)&&(i<l))    {        j=0;        for(;j<ll&&i+j<l&&a[i+j]==b[j];++j);        if(j==ll)        {            cout<<i+1<<"\n";            i++;            j=0;            continue;        }        if(i+ll>=l)          return ;        i+=nxt[a[i+ll]];    }    return ;}int main(){    cin>>a>>b;    l=strlen(a);ll=strlen(b);    fai(ll);    Sunday();    for(int i=0;i<ll;i++)      cout<<pre[i]+1<<" ";    return 0;}

一共交了50多次
我估计我要入黑名单了
GG

神奇的kmp其实如果只是匹配字符串,直接暴力求next数组就可以。
但是对于一些next数组的实际应用方面就可以体现kmp的精华

poj2752

一道裸题

Seek the Name, Seek the Fame
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 19178 Accepted: 9854

Description
The little cat is so famous, that many couples tramp over hill and dale to Byteland, and asked the little cat to give names to their newly-born babies. They seek the name, and at the same time seek the fame. In order to escape from such boring job, the innovative little cat works out an easy but fantastic algorithm:

Step1. Connect the father’s name and the mother’s name, to a new string S.
Step2. Find a proper prefix-suffix string of S (which is not only the prefix, but also the suffix of S).

Example: Father=’ala’, Mother=’la’, we have S = ‘ala’+’la’ = ‘alala’. Potential prefix-suffix strings of S are {‘a’, ‘ala’, ‘alala’}. Given the string S, could you help the little cat to write a program to calculate the length of possible prefix-suffix strings of S? (He might thank you by giving your baby a name:)

Input
The input contains a number of test cases. Each test case occupies a single line that contains the string S described above.

Restrictions: Only lowercase letters may appear in the input. 1 <= Length of S <= 400000.

Output
For each test case, output a single line with integer numbers in increasing order, denoting the possible length of the new baby’s name.

Sample Input

ababcababababcabab
aaaaa

Sample Output

2 4 9 18
1 2 3 4 5

看似复杂实际上不过是求出最后一个字符的next并一直跳他的next

#include<stdio.h>#include<string.h>#include<cstdio>int next[400005];char s[400005];int sum[400000];void get_next(int len){    int i,j;    i=0;    j=-1;    next[0]=-1;    while(i<len)    {        if(j==-1||s[i]==s[j])        {            ++i;            ++j;            next[i]=j;        }        else            j=next[j];    }}int main(){    freopen("1.in","r",stdin);    freopen("1.out","w",stdout);    int len,i,k;    while(scanf("%s",s)!=EOF)    {        k=0;        len=strlen(s);        get_next(len);        for(i=len;i!=0;)        {            sum[k++]=next[i];            i=next[i];        }        for(i=k-2;i>=0;--i)            printf("%d ",sum[i]);        printf("%d\n",len);    }    return 0;}

一道依然很水的题
noi2014动物园
具体思路,记下每一个数的next数组和 往前跳 的位置
对于每个字符如果这个字符的next的位置大于一半的位置就向前跳
找到合适的位置就乘

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int num[1000005],nex[1000005];char ch[1000005];int n;long long ans;void kmp(){    int l=strlen(ch+1);    int fix=0;    num[1]=1;    for(int i=2;i<=l;i++)    {        while(ch[fix+1]!=ch[i]&&fix) fix=nex[fix];        if(ch[fix+1]==ch[i]) fix++;        nex[i]=fix;        num[i]=num[fix]+1;    }    fix=0;    for(int i=2;i<=l;i++)    {        while(ch[fix+1]!=ch[i]&&fix) fix=nex[fix];        if(ch[fix+1]==ch[i]) fix++;        while((fix<<1)>i&&nex) fix=nex[fix];        ans=ans*(num[fix]+1)%1000000007;    }    return ;}int main(){    cin>>n;    while(n--)    {        scanf("%s",ch+1);        ans=1;        kmp();        printf("%lld\n",ans);    }    return 0; } 

说真的发现怎么写kmp超简单

2 0
原创粉丝点击