kmp&扩展kmp&manacher 学习

本文深入解析了KMP、扩展KMP及Manacher算法,详细介绍了这些算法在字符串匹配问题中的应用,包括求模式串在文本串中的位置、求最长公共前缀以及寻找最长回文子串等。通过具体实例和代码实现,帮助读者掌握不同场景下的字符串匹配技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

kmp: 求出模式串在文本串中第一次出现的位置 O(n+m)

扩展kmp: 求出模式串与文本串中每个i为下标的后缀串的最长公共前缀 extend[i] O(n+m)

manacher:求出母串中的最长回文子串O(n) 

kmp模板:

#include<cstdio>//O(m + n) m模式串 n文本串
#include<iostream>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<cstring>
#include<string>
using namespace std;
const int maxn=100005;
int nxt[maxn];
char x[maxn];
char y[maxn];

void getnext(char *p,int nxt[])
{
    int plen=strlen(p);
    nxt[0]=-1;
    int k=-1;
    int j=0;
    while(j<plen-1)
    {
        if(k==-1||p[k]==p[j])
        {
            ++k;
            ++j;
            if(nxt[j]!=nxt[k])
                nxt[j]=k;
            else
                nxt[j]=nxt[k];
        }
        else
        {
            k=nxt[j];
        }
    }
}
int kmpsearch(char *s, char *p)
{
    int i=0;
    int j=0;
    int slen=strlen(s);
    int plen=strlen(p);
    while(i<slen&&j<plen)
    {
        if(j==-1||s[i]==p[j])
            i++,j++;
        else
            j=nxt[j];
    }
    if(j==plen)return i-j;
    else return -1;
}

int main()
{
    scanf("%s",x);
    scanf("%s",y);
    getnext(y,nxt);
    cout<<kmpsearch(x,y)<<endl;
    return 0;
}

扩展kmp:

#include<cstdio>
#include<iostream>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<cstring>
#include<string>
using namespace std;
const int maxn=100005;
int nxt[maxn];
int extend[maxn];
char s[maxn];
char t[maxn];
void getnext(char *s,int nxt[])
{
    int nn = strlen(s);
    nxt[0] = nn;
    int p = 0;
    while (p+1 < nn && s[p] == s[p+1]) p++;
    nxt[1] = p;
    int k = 1, L;
    for (int i = 2; i < nn; i++)
    {
        p =     k + nxt[k] - 1; L = nxt[i - k];
        if (i + L <= p) nxt[i] = L;
        else
        {
            int j = p - i + 1;
            if (j < 0) j = 0;
            while (i + j < nn && s[i + j] == s[j]) j++;
            nxt[i] = j; k = i;
        }
    }
}

void getextend(char *s,char *t,int extend[])
{
    int nn = strlen(s) ,mm = strlen(t);
    getnext(s,nxt);
    int p = 0;
    while (p < nn && s[p] == t[p]) p++;
    extend[0] = p;
    int k = 0, L;
    for (int i = 1; i < nn; i++)
    {
        p = k + extend[k] - 1;
        L = nxt[i - k];
        if (i + L <= p) extend[i] = L;
        else
        {
            int j = p - i + 1;
            if (j < 0) j = 0;
            while (i + j < nn && s[i + j] == t[j]) j++;
            extend[i] = j; k = i;
        }
    }
}
int main()
{
    scanf("%s",s);
    scanf("%s",t);
    getnext(s,nxt);
    getextend(s,t,extend);
    int len=strlen(s);
    for(int i=0;i<len;i++)
    {
        cout<<extend[i]<<endl;
    }
    return 0;
}

Manacher:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
using namespace std;
string a;
string Manacher(string s)
{
    string res="$#";
    for(int i=0;i<s.size();++i)
    {
        res+=s[i];
        res+="#";
    }
    vector<int> P(res.size(),0);
    int mi=0,right=0;
    int maxLen=0,maxPoint=0;

    for(int i=1;i<res.size();++i)
    {
        P[i]=right>i ?min(P[2*mi-i],right-i):1;

        while(res[i+P[i]]==res[i-P[i]])
            ++P[i];

        if(right<i+P[i])
        {
            right=i+P[i];
            mi=i;
        }

        if(maxLen<P[i])
        {
            maxLen=P[i];
            maxPoint=i;
        }
    }
    return s.substr((maxPoint-maxLen)/2,maxLen-1);
}
int main()
{
	char b[1010002];
	//while(getline(cin,a)) 可以读空行 
	//while(~scanf("%s",b)) a=b  不可以读空行 
    //cout<<Manacher(a)<<endl;  Manacher(a).length() 
    return 0;
}

第一题:求模式串在文本串中出现的次数

HDU - 1686 

做法考虑一下next数组的意义,i对于母串,j对于模式串。如果i和j匹配成功,并且j是模式串的最后一个位置。

我们考虑j+1和i+1,j+1明显为空,i+1和j+1失配,这时我们只要知道j=nxt[j+1],就可以开始下一次的匹配。每次

j=plen的时候累计答案。

#include<cstdio>//O(m + n) m模式串 n文本串
#include<iostream>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<cstring>
#include<string>
using namespace std;
const int maxn=10005;
int nxt[maxn];
char x[1000006];
char y[maxn];
int ans=0;
void getnext(char *p,int nxt[])
{
    memset(nxt,-1,sizeof(-1));
    int plen=strlen(p);
    nxt[0]=-1;
    int k=-1;
    int j=0;
    while(j<plen)
    {
        if(k==-1||p[k]==p[j])
        {
            ++k;
            ++j;
            nxt[j]=k;
        }
        else
        {
            k=nxt[k];
        }
    }
}
int kmpsearch(char *s, char *p)
{
    ans=0;
    int i=0;
    int j=0;
    int slen=strlen(s);
    int plen=strlen(p);
    while(i<slen)
    {
        if(j==-1||s[i]==p[j])
            i++,j++;
        else
            j=nxt[j];
        if(j==plen)
        {
            ans++;
            j=nxt[j];
        }
    }
    return ans;
}

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        scanf("%s",y);
        scanf("%s",x);
        getnext(y,nxt);
        cout<<kmpsearch(x,y)<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值