• 蓝桥杯倒计时 41天 - KMP 算法


    KMP算法

    KMP算法是一种字符串匹配算法,用于匹配模式串P在文本串S中出现的所有位置。
    例如S=“ababac,P=“aba”,那么出现的所有位置是13。
    在初学KMP时,我们只需要记住和学会使用模板即可,对其原理只需简单理解,不要过度深究,避免把自己绕进去,可以课后自己慢慢去画图理解。
    KMP算法将原本O(n2)的字符串匹配算法优化到了O(n).其精髓在于next数组,next数组表示此时模式串下标失配时应该移动到的位置,也表示最长的相同真前后缀的长度。
    在这里插入图片描述
    例如P=“ababac”,S=“abababac”。
    当匹配到i=6,j=5,P[i+1]!=S[i]时,j不会移动到1重新开始匹配,而是移动到nex[j]=3继续匹配,
    则接下来i=6,j=3,有P[j+1]=S[i],成功匹配,则i,j继续后移,直到i=8.j=6完成一次匹配,则P在S中第一次出现的位置为j-i+1=3。

    计算next数组(next数组仅与模式串P有关)的方式就是用P自己去匹配自己,大家只需要掌握模板即可,暂时不要深究其原理。

    char s[N],p[N];
    int nex[M];
    int n = strlen(s+1),m=strlen(p+1);//字符串下标从 1 开始
    nex[0]=nex[1]=0;
    for(int i=2,j=0;i<=m;++i){
    	while(j&&p[i]!=p[j+1])j=nex[j];
    	if(p[i]==p[j+1])j++;//从 while 出来后要么 j=0,要么 p[i]==p[j+1],如果匹配成果,则 j 后移
    	nex[i]=j;//如果匹配失败就回到 j,因为此时 p[1~j]=p[i-j+1~j]或 j=0(回到最初的地方开始匹配)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    通过 next 数组匹配

    for(int i=1,j=0;i<=n;i++)
    {
    	while(j&&s[i]!=p[j+1])j=nex[j];
    	if(s[i]==p[j+1])j++;
    	if(j==m)//成功匹配一次
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    斤斤计较的小Z

    在这里插入图片描述
    思路:KMP 算法模板,不知道为啥结果不对

    #include
    using namespace std;
    const int N =20,M=20;
    char s[N],p[M];
    int nex[M];
    
    int main(){
        scanf("%s\n%s",p+1,s+1);
        int n=strlen(s+1),m=strlen(p+1);
        nex[0]=nex[1]=0;
        for(int i=2,j=0;i<=m;i++){
            while(j&&p[j+1]!=p[i])j=nex[j];
            if(p[j+1]==p[i])j++;
            nex[i]=j;
        }
        int res=0;
        for(int i=1,j=0;i<=n;i++){
            while(j&&p[j+1]!=s[i])j=nex[j];
            if(p[j+1]==s[i])j++;
            if(j==m)res++;
        }
        cout<<res<<'\n';
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    boarder

    在这里插入图片描述
    思路:利用 KMP 求整个串的最长真前后缀,len-nex[len]就是整个串的循环节,len 能整除循环节就是答案,不能就是 1。

    #include
    using namespace std;
    const int N=1e6+10;
    char p[N];
    int nex[N];
    int main( ){
        scanf("%s",p+1);
        unsigned long m=strlen(p+1);
        nex[0]=nex[1]=0;
        for(int i=2,j=0;i<=m;i++){
            while(j&&p[i]!=p[j+1])j=nex[j];
            if(p[i]==p[j+1])j++;
            nex[i]=j;
        }
        int len=m-nex[m];
        if(m%len==0){
            cout<<m/len<<endl;
        }else{
            cout<<1<<endl;
        }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    幸运字符串

    在这里插入图片描述
    在这里插入图片描述
    思路:求 nex 数组,找最大值就是答案

    #include
    using namespace std;
    const int N = 2e5+10;
    int n;
    char p[N];
    int nex[N];
    int main( ){
        cin>>n;
        scanf("%s",p+1);
        unsigned long m=strlen(p+1);
        for(int i=2,j=0;i<=m;i++){
            while(j&&p[i]!=p[j+1])j=nex[j];
            if(p[i]==p[j+1])j++;
            nex[i]=j;
        }
        int ans=0;
        for(int i=1;i<=m;i++)ans=max(ans,nex[i]);
        cout<<ans<<endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    你也喜欢幸运字符串吗?

    在这里插入图片描述
    思路:动态规划+KMP,不会。
    在这里插入图片描述

    #include 
    #define ll long long
    #define PI 3.1415926
    using namespace std;
    typedef pair<int, int> vt;
    typedef pair<vt, vt> PII;
    const int N = 1e6 + 10;
    const int M = 2 * N;
    const int mod = 998244353;
    const ll INF = 0x3f3f3f3f3f3f3f3f;
    
    int ne[N];
    string s;
    int n;
    
    void solve()
    {
        cin >> n;
        cin >> s;
        memset(ne, 0, sizeof ne);
        s = " " + s;
    
        for (int i = 2, j = 0; i <= n; i++)
        {
            while (j && s[i] != s[j + 1])
                j = ne[j];
    
            if (s[i] == s[j + 1])
                j++;
    
            ne[i] = j;
        }
    
        vector<ll> f(n + 5);
        for (int i = 1; i <= n; i++)
            f[i] = 1;
    
        for (int i = n; i >= 1; i--)
            f[ne[i]] += f[i];
    
        ll ans = 0;
        // for(int i=1;i<=n;i++)cout<
        for (int i = 1; i <= n; i++)
        {
            if (ne[i] != 0)
                ans += f[ne[i]];
        }
        cout << ans << endl;
    }
    
    signed main()
    {
        ios::sync_with_stdio(false);
        /*多组案例初始化*/
        // int t;cin>>t;while(t--)
        solve();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
  • 相关阅读:
    FTPS 227 Entering Passive Mode
    微信小程序项目开发Day1
    C#集成ViewFaceCore人脸检测识别库
    vue3 + antdv table 展开行 expandedRowRender 根据判断条件动态显隐展开行的 icon
    JDBC连接
    基于非线性收敛因子和局部扰动的鲸鱼算法-附代码
    APS生产排产在包装行业的应用
    ASP.NET Core 6框架揭秘实例演示[02]:基于路由、MVC和gRPC的应用开发
    python unittest测试报告生成
    外观 ( Facade ) 模式
  • 原文地址:https://blog.csdn.net/qq_61735602/article/details/136424526