Wunschの小窝
首页项目归档照片墙音乐灵境说说杂谈友链关于
封面

KMP

写作时间:2026-05-19 21:20:43
# 算法学习笔记

思想

KMP的名字是取了三个发明者名字的首字母,用于解决单模式串匹配问题,即给定一个文本串(text)和一个模式串(pattern),在文本串中找出所有与模式串完全相同的位置(或判断是否存在、统计次数等)。

‍

最朴素的办法为双指针,复杂度为O(M×N)O(M \times N)O(M×N)。

‍

KMP的基本思路为当我们发现某一个字符不匹配时,由于已经知道之前遍历过的字符,考虑利用这些信息来避免回退这个步骤,即使文本串指针一直向右。

‍

nextnextnext数组的定义为最长公共前后缀用于实现避免回溯的功能,我们通常称为失配数组,建立于模式串之上,用于记录在当前位置失配后应该跳转到哪个位置。

‍

求nextnextnext数组就是个简单的递推。如果相同就自增,如果不同就跳到上一个nextnextnext,也就是最长公共前后缀的最长公共前后缀,即找到原串长度次之的公共前后缀,继续匹配。

‍

复杂度为O(M+N)O(M+N)O(M+N)

题

P3375 【模板】KMP - 洛谷

‍模板题。

ACCode:AC Code:ACCode:

#include<iostream>
#include<queue>
#include<deque>
#include<bitset>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<unordered_map>
#include<set>
#include<map>
#include<ctime>
#include<random>
#include<vector>
#include<utility>
#define f(i,a,b) for(ll i=a;i<=b;i++)
using ll = long long;
using db = double;
ll rd(){ll x=0,w=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-'){w=-1;}c=getchar();}while('0'<=c&&c<='9'){x=x*10+(c-'0');c=getchar();}return x*w;}
using namespace std;
const ll MX =5e5+7;
const ll mod = 1e9+7;
mt19937 mrd(time(0));
namespace Wunsch{

    ll n,T,k,m,ans,cnt;
    ll nxt[MX];
    string s1,s2;
    void work(){
        cin>>s1>>s2;
        s1=" "+s1;
        s2=" "+s2;
        ll j=0;
        f(i,2,s2.size()-1){
            while(j&&s2[i]!=s2[j+1])j=nxt[j];
            if(s2[i]==s2[j+1])j++;
            nxt[i]=j;
        }
        j=0;
        f(i,1,s1.size()-1){
            while(j&&s2[j+1]!=s1[i])j=nxt[j];
            if(s1[i]==s2[j+1])j++;
            if(j==s2.size()-1)cout<<i-s2.size()+2<<endl;
        }
        f(i,1,s2.size()-1){
            cout<<nxt[i]<<" ";
        }
        return ;
    }

    bool main(){
        T=1;
        while(T--)work();
        return 1;
    }
}
bool amiya=Wunsch::main();
int main(){;}

‍懒癌犯了明天应该会补一些题,现在去学一会乐理去。

updon26.05.22upd on 26.05.22updon26.05.22

数一数

我以为是神必计数题,没想到剪枝就过了。

设nnn个字符串中长度最小为mmm。

首先发现长度大于mmm的字符串没法作为长度为mmm的字符串的子串,所以答案为000。

然后发现存在多个长度为mmm的字符串时如果这些字符串不相等那么他们的答案也为000。

所以最多只需要做一次KMPKMPKMP就可以了。

ACCode:AC Code:ACCode:

#include<iostream>
#include<queue>
#include<deque>
#include<bitset>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<unordered_map>
#include<set>
#include<map>
#include<ctime>
#include<random>
#include<vector>
#include<utility>
#define f(i,a,b) for(ll i=a;i<=b;i++)
using ll = long long;
using db = double;
ll rd(){ll x=0,w=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-'){w=-1;}c=getchar();}while('0'<=c&&c<='9'){x=x*10+(c-'0');c=getchar();}return x*w;}
using namespace std;
const ll MX =2e6+7;
const ll mod = 998244353;
mt19937 mrd(time(0));
namespace Wunsch{

    ll n,T,k,m,ans,cnt,nm;
    ll nxt[MX];


    ll j;
    string s[1000010],ss;

    void kmp(ll x){
        j=0;
        f(i,2,s[x].size()-1){
            while(j&&s[x][i]!=s[x][j+1])j=nxt[j];
            if(s[x][i]==s[x][j+1])j++;
            nxt[i]=j;
        }
        return ;
    }

    void work(){
        n=rd();
        m=1145141919810;
        f(i,1,n){
            cin>>s[i];
            s[i]=" "+s[i];
            m=min(m,(ll)s[i].size());
        }
        ll num=0;
        f(i,1,n){
            if(s[i].size()==m){
                num++;
                ss=s[i];
                nm=i;
            }
        }
        if(num>1){
            f(i,1,n){
                if(s[i].size()==m&&s[i]!=ss){
                    f(o,1,n){
                        puts("0");
                    }
                    return ;
                }
            }
        }
        j=0;
        kmp(nm);
        ans=1;
        f(k,1,n){
            j=0;
            cnt=0;
            f(p,1,s[k].size()-1){
                while(j&&s[k][p]!=s[nm][j+1])j=nxt[j];
                if(s[k][p]==s[nm][j+1])j++;
                if(j==s[nm].size()-1){
                    cnt++;
                    j=nxt[j];//不加这一步有越界风险,不过大部分时候可有可无
                }
            }
            ans*=cnt;
            ans%=mod;
        }
        f(i,1,n){
            if(m!=(ll)s[i].size()){
                cout<<"0\n";
            }
            else {
                cout<<ans<<"\n";
            }
        }
        return ;
    }

    bool main(){
        // T=rd();
        T=1;
        while(T--)work();
        return 1;
    }
}
bool amiya=Wunsch::main();
int main(){;}

栗酱的数列

还是很简单的,手算算样例就出来了。

考虑转化为差分约束。

设这个公共余数为 (r)( r )(r),则对于任意 (i)( i )(i) 有

(ai+bi) mod k=r(a\\_i + b\\_i) \bmod k = r(ai​+bi​)modk=r

考虑相邻两项的差:

((ai+1+bi+1)−(ai+bi)) mod k=0\big((a\\_{i+1} + b\\_{i+1}) - (a\\_i + b\\_i)\big) \bmod k = 0((ai+1​+bi+1​)−(ai​+bi​))modk=0

展开得

((ai+1−ai)+(bi+1−bi)) mod k=0\big((a\\_{i+1} - a\\_i) + (b\\_{i+1} - b\\_i)\big) \bmod k = 0((ai+1​−ai​)+(bi+1​−bi​))modk=0

记

dai=(ai+1−ai) mod k,dbi=(bi+1−bi) mod kda\\_i = (a\\_{i+1} - a\\_i) \bmod k,\quad db\\_i = (b\\_{i+1} - b\\_i) \bmod kdai​=(ai+1​−ai​)modk,dbi​=(bi+1​−bi​)modk

则条件变为

(dai+dbi) mod k=0对于 i=1,2,…,m−1(da\\_i + db\\_i) \bmod k = 0 \quad \text{对于 } i = 1,2,\dots,m-1(dai​+dbi​)modk=0对于 i=1,2,…,m−1

即

dai≡− dbi(modk)da\\_i \equiv -\,db\\_i \pmod kdai​≡−dbi​(modk)

ACCode:AC Code:ACCode:

#include<iostream>
#include<queue>
#include<deque>
#include<bitset>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<unordered_map>
#include<set>
#include<map>
#include<ctime>
#include<random>
#include<vector>
#include<utility>
#define f(i,a,b) for(ll i=a;i<=b;i++)
using ll = long long;
using db = double;
ll rd(){ll x=0,w=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-'){w=-1;}c=getchar();}while('0'<=c&&c<='9'){x=x*10+(c-'0');c=getchar();}return x*w;}
using namespace std;
const ll MX =2e5+7;
const ll mod = 998244353;
mt19937 mrd(time(0));
namespace Wunsch{

    ll n,T,k,m,ans,cnt,nm;
    ll nxt[MX];
    ll a[MX],b[MX];


    ll j;

    void work(){
        n=rd();m=rd();k=rd();
        ans=0;
        f(i,1,n)a[i]=rd();
        f(i,1,m)b[i]=rd();
        f(i,1,n-1){
            a[i]=((a[i+1]-a[i])%k+k)%k;
            // cout<<a[i]<<" ";
        }
        // cout<<endl;
        f(i,1,m-1){
            b[i]=((b[i+1]-b[i])%k+k)%k;
            // cout<<b[i]<<" ";
        }
        // cout<<endl;
        j=0;
        f(i,2,m-1){
            while(j&&b[i]!=b[j+1])j=nxt[j];
            if(b[i]==b[j+1])j++;
            nxt[i]=j;
        }
        // ll p=-1;
        j=0;
        f(i,1,n-1){
            while(j&&(a[i]+b[j+1]%k+k)%k!=0)j=nxt[j];
            // if(p==-1)p=(a[i]+b[j+1])%k;
            if(((a[i]+b[j+1])%k+k)%k==0)j++;
            if(j==m-1){
                ans++;
                j=nxt[j];
                // p=-1;
            }
        }
        cout<<ans<<endl;
        f(i,1,m-1)nxt[i]=0;
        return ;
    }

    bool main(){
        T=rd();
        // T=1;
        while(T--)work();
        return 1;
    }
}
bool amiya=Wunsch::main();
int main(){;}

‍

avatar

Wunsch

蠕动的区

RECOMMENDED

基础乐理学习笔记(持续更新中)

2026-05-01 21:25:45

Edu 190

2026-05-19 11:26:30

并查集

2026-05-22 14:42:47

Table of Contents