zoukankan      html  css  js  c++  java
  • 51NOD 1292 1277(KMP算法,字符串中的有限状态自动机)

    在前两天的CCPC网络赛中。。。被一发KMP题卡了住了。。。遂决定,哪里跌倒就在哪里爬起来。。。把个KMP恶补一发,连带着把AC自动机什么的也整上。

    首先,介绍设定:KMP算法计划解决的基本问题是,两个不同字符串间的匹配问题。

    例如:

    求字符串:JSADLKFMNALDGABJSDF;QSDLKJG;KERJG'ERPIWHEFCNKDSBVJKN LKGBLKM,ACFL

    中 KASJDGNKAJ出现了几次?

    当然上面的两个字符串都是滚键盘滚出来的恩。。。

    但是直观地使用对比的方式来强行进行比对的话需要O(M*N)的时间复杂度,在两个字符串的长度逐渐增长的大背景下是很难以接受的。例如(串1长50000串2长20000)分分钟炸。

    于是这个时候我们需要KMP来歼灭即将爆管的时间复杂度。处理方式,就是将底下的子字符串变成一个“有限状态自动机”,从而避免进行重复的无用匹配。具体做法是,对于输入字符串,开同样大小的数组F[MAXN]表示失配边,对于任意一个匹配失败的元素K,F[K]将指向“K元素之前,和K元素有最长公共,前缀的位置”,值得注意的是,KMP算法并没有对该位置是否符合tar[K]!=tar[F[K]]的规约和判断,这意味着,我们不能认为“F[K]指向上一个,与F[K]拥有最长公共前缀的,且不相同的子串”。这一点是我在学习KMP中的一个最开始带入的想当然的设定,其实很好想明白,就是,无论F[K]指向了什么值,最终都会有失配的可能性,遇到这种可能性之后往上一个失配点走就是了,没必要对这种可能性做特殊处理。

    对于这道题来说,情况有些特殊,首先应当把字符串本身处理成一个有限状态自动机,之后每次对于上一次出现的位数进行加和操作,最终统计最大值。首先上两个参数不同的AC代码。

    #include<bits/stdc++.h>
    using namespace std;
    
    const long long MAXN=1000233;
    long long f[MAXN];
    char tar[MAXN];
    long long point[MAXN];
    long long len;
    
    void init()
    {
        scanf("%s",tar+1);
        len=strlen(tar+1);
        int k=0;
        for(int i=2;i<=len;++i)
        {
            while(k&&tar[k+1]!=tar[i])k=f[k];
            if(tar[k+1]==tar[i])k++;
            f[i]=k;
            point[k]++;
        }
    }
    
    int main()
    {
        init();
        long long ans=0;
        for(int i=len;i;i--)
        {
            ans=max(ans,(long long)i*(point[i]+1));
            point [f[i]]+=point[i];
        }
        cout<<ans<<endl;
    }
    #include<bits/stdc++.h>
    using namespace std;
    
    const long long MAXN=1000233;
    long long f[MAXN];
    long long point[MAXN];
    char tar[MAXN];
    long long len;
    
    void init()
    {
        gets(tar);
        len=strlen(tar);
        f[0]=0;f[1]=0;
        for(int i=1;i<len;++i)
        {
            int j=f[i];
            while(j&&tar[i]!=tar[j])j=f[j];
            f[i+1]= tar[i]==tar[j]? j+1:0;
        }
    }
    
    int main()
    {
        cin.sync_with_stdio(false);
        init();
        for(int i=0;i<len;++i)
        {
            point[i]=1;
        }
        for(int i=len;i;--i)
        {
            if(f[i]&&i)
            point[f[i]-1]+=point[i-1];
        }long long ans=0;
        for(int i=0;i<len;++i)
        {
            ans=max((long long)(i+1)*point[i],ans);
        }
        cout<<ans<<endl;
        return 0;
    }

    代码1中使用了对于F[K]的规约是:F[K]等于与K拥有包括K、F[K]本身的最长公共前缀的元素

    代码2(刘汝佳蓝书)使用的F[K]代表,与K元素拥有  “    绝对不  ”   包括K、F[K]在内的拥有最长公共前缀的元素,这也意味着需要取得字符串后一个位置。

  • 相关阅读:
    C语言I博客作业09
    C语言I博客作业08
    C语言I作业07
    C语言博客作业06
    C语言I博客作业05
    C语言I博客作业04
    C语言I博客作业03
    C语言|博客作业07
    C语言|博客作业06
    C语言|博客作业05
  • 原文地址:https://www.cnblogs.com/rikka/p/7413603.html
Copyright © 2011-2022 走看看