KMP模板

参考视频:https://www.bilibili.com/video/av3246487/

https://www.bilibili.com/video/av11866460/

https://www.bilibili.com/video/av16828557/

参考博客:https://subetter.com/algorithms-and-mathematics/kmp-algorithm.html

复杂度:

//s为母串,p为模式串
void getnext()
{
    int k=-1,j=0;
    int len=strlen(p);
    Next[0]=-1;
    while(j<len)
    {
        if(k==-1||p[k]==p[j])
        {
            k++;
            j++;
            //if(p[j]!=p[k])
            Next[j]=k;
            //else Next[j]=Next[k];
        }
        else k=Next[k];
    }
}
int KMP()
{
    getnext();
    int i=0,j=0,cnt=0;
    int slen=strlen(s);
    int plen=strlen(p);
    while(i<slen)
    {
        if(j==-1||s[i]==p[j])
        {
            i++;
            j++;
            if(j==plen)
            {
                printf("%d\n",i+1-plen);//出现的位置
                cnt++;
                j=Next[j];
            }
        }
        else j=Next[j];
    }
    return cnt;   //返回出现的次数
}
Manacher模板

参考博客:https://www.jianshu.com/p/799bc53d4e3d

复杂度:

char a[11000010];//aba
char anew[23000010];//$#a#b#a#
int p[23000010];//新串中以第i个字符为对称轴的回文串的回文半径
//p[i]-1为原串中以i为对称轴的最长回文串的长度

/*
* abaaba
* i: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
* anew[i]: $ # a # b # a # a # b # a #
* p[i]:    1 1 2 1 4 1 2 7 2 1 4 1 2 1
* p[i]-1:  0 0 1 0 3 0 1 6 1 0 3 0 1 0
*/

void init()
{
    int len=strlen(a);
    anew[0]='$';
    anew[1]='#';
    int num=2;
    for(int i=0;i<len;i++)
    {
        anew[num++]=a[i];
        anew[num++]='#';
    }
    anew[num]='\0';
    //puts(anew);
}
int Manacher()
{
    init();
    int MAX=-1;
    int len=strlen(anew);
    //printf("%d\n",len);
    int id;//mx对应的回文串的对称轴所在的位置
    int mx=0;//当前访问到的所有回文子串,所能触及的最右一个字符的位置
    for(int i=1;i<len;i++)
    {
        if(i<mx)
            p[i]=min(p[2*id-i],mx-i);
        else
            p[i]=1;
        while(anew[i-p[i]]==anew[i+p[i]])//扩展
            p[i]++;
        if(mx<i+p[i])//最右的字符的位置要改变
        {
            id=i;
            mx=i+p[i];
        }
        MAX=max(MAX,p[i]-1);
    }
    return MAX;
}
int main()
{
    scanf("%s",a);
    printf("%d\n",Manacher());
    /*for(int i=1;i<strlen(anew);i++)
        printf("%d ",p[i]-1);
    printf("\n");*/
    return 0;
}
HDU 1686

题意:AZA AZAZAZA 输出3

解题思路:

if(j==plen)
{
    cnt++;
    j=Next[j];
}
HDU 2087

题意:aaaaaa aa 输出3

解题思路:

if(j==plen)
{
    cnt++;
    j=0;
}
HDU 3746

题意:给你一个字符串,要求将字符串的全部字符最少循环2次需要添加的字符数。

解题思路:找循环节。len-next[i]为此字符串的最小循环节(i为字符串的结尾),如果len%(len-next[i])==0,此字符串的最小周期就为len/(len-next[i])。

int length=len-Next[len];//循环节长度
if(len!=length&&len%length==0)
    printf("0\n");
else printf("%d\n",length-Next[len]%length);
HDU 3336(存疑)

题意:求给出字符串的所有前缀在原字符串中出现的次数和。

解题思路:dp[i]代表以第i位结尾的字符串中包括多少以第i位字母结尾的前缀的出现次数,那么有dp[i]=dp[Next[i]]+1,利用Next数据跳转到第Next[i]的位置,这个位置的跟之前的有过多少次匹配,只要在原基础上加1即可。

HDU 2609

题意:有n个有01组成的字符串,每个字符串都代表一个项链,那么该字符串就是一个环状的结构,求可以经过循环旋转,最后不同的串有多少个。

解题思路:

将每个字符串转换成最小串(因为最小串对于每一个长度相同但不同的串都是不一样的),然后放在set里面去重。

最小表示法:

有一个字符串,这个字符串的首尾是连在一起的,要求寻找一个位置,以该位置为起点的字符串的字典序在所有的字符串中中最小。

假设有两个下标i,j,表示如果从i和从j出发的字符串,有一个k表示字符串的长度,如果长度达到len,就表示找到最小的串。

s[i+k] == s[j+k]: k++

s[i+k]>s[j+k]: i=i+k+1 表示以i,到i+k为起点的字符串,都不是最小字符串的前缀。

s[i+k]<s[j+k]: j=j+k+1 同理

直到i或j大于串长,找较小者。

注意:1.i和j一定是不同的。2.每次不等时,需要设置k为0。

复杂度:

最小表示法模板
int findMin()
{
    int len=str.size();
    int i=0,j=1,k=0;
    while(i<len&&j<len&&k<len)
    {
        if(str[(i+k)%len]==str[(j+k)%len])
            k++;
        else if(str[(i+k)%len]>str[(j+k)%len])   //最大表示法这里变成<
        {
            i=i+k+1;
            k=0;
        }
        else
        {
            j=j+k+1;
            k=0;
        }
        if(i==j)j++;
    }
    return min(i,j);   //返回的是第一个最小串的首字符的位置
}
HDU 4513

题意:求最长回文子串,而外要求:从回文串最中间向两边满足非递增。

解题思路:改变一下扩展条件即可。

        while(anew[i-p[i]]==anew[i+p[i]])//扩展
        {
            if(anew[i-p[i]]!=-2)
            {
                if(anew[i+p[i]]<=anew[i+p[i]-2])p[i]++;
                else break;
            }
            p[i]++;
        }
HDU 3294

字符串转化的时候不用刚开始就转化,要判断好回文以后输出的地方再转化,否则会TLE。

2018年全国多校算法寒假训练营练习比赛(第五场)C

题意:有一个字符串,让你找到这个字符串 S 里面的子串T,必须满足是这个串的前缀,也是这个串的后缀,并且在字符串中也出现过一次的,输出最长满足要求字符串。

思路:利用Next数组,next数组表示的是这个串最长的公共前后缀,那么求出这个串所有的公共前后缀就是循环即可,因为最长的公共前后缀是next[len],那么比它短一点的肯定是在这个前缀的最长前后缀,即next[next[len]],然后KMP一下如果出现次数大于等于3的话就符合要求。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

AC自动机专题 Previous
数论基础专题 Next