算法竞赛进阶指南——0x15【字符串】

本文介绍了KMP匹配模式,重点讲解了如何通过Next数组求解以及两种优化方法(Next数组与Nextval数组)。最小表示法部分阐述了如何利用循环同构串快速找到最优匹配。总结了KMP算法的核心思想和优化策略。
摘要由CSDN通过智能技术生成


在这里插入图片描述

KMP匹配模式

勇敢🐂🐂,不怕困难
关键在于求next数组

时间复杂度为O(m+n),空间复杂度记为O(m)
主要思想:以空间换时间,减少回溯次数

  • next[i]=j,含义是:下标为i 的字符串最长相等前后缀的长度为j,且A[i-j+1i]=A[1j]。求解next数组有点动态规划的思想
  • 每一个字符前的字符串都有最长相等前后缀,而且最长相等前后缀的长度是我们移位的关键可以理解为模式串在文本串下面移动
  • 引理1.若j0是next[i]的一个候选项,即j0<i且A [i-j0+1~i] = A [1~j0],则 < j0的最大的next[i]的候选项是next[j0]
  • 引理2.对于字符串s,s[1~i]具有长度为len< i的循环元<=>len能整除i且s[1~i-len] = s[len+1~i]【即i-len是next[i]的候选项】
void getnext(char pattern[], ll nex[])
{
    int i = 0, j = -1, len = n;
    nex[0] = -1; //next有冲突,要用Next,使得Next数组整体向右移动了一位,这样的话,len=Next[len-1]变为len=Next[len]
    while (i < len)    //注意不是i<len-1
    {
        if (j == -1 || pattern[i] == pattern[j])
        {
            i++, j++;//i表示当前位置,j表示最长公共前缀子串的后一个字符
            nex[i] = j;
        }
        else
            j = nex[j];//回溯
    }
}
int kmp(char text[], char pattern[])//KMP算法,模式串、文本串
{
    int lent = strlen(text), lenp = strlen(pattern);
    int i = 0, j = 0;//i、j均初始化为0
    get_next(pattern, Next);//记得用Next数组
    while (i <= lent)//注意不是i<lent&&j<lenp
    {
        if (j == -1 || text[i] == pattern[j])
        {
            i++;
            j++;
        }
        else
            j = Next[j];//回溯过程,i不变,j后退
    }
    if (j >= lenp)
        return i - lenp;//返回匹配模式下的首字母下标
    else
        return -1;//返回-1表示未匹配
}

优化后,但是不建议使用

char pattern[N], text[N];
int Nextval[N];
void getnextval(char pattern[], int Nextval[]) //构造Nextval数组
{
    int i = 0, j = -1, len = strlen(pattern); //初始化i=0、j=-1
    Nextval[0] = -1;                         
    while (i < len)                           //注意不是i<len-1
    {
        if (j == -1 || pattern[i] == pattern[j])
        {
            i++; //i表示当前位置
            j++; //j表示最长公共前缀子串的后一个字符
            if (pattern[i] != pattern[j])
                Nextval[i] = j;
            else
                Nextval[i] = Nextval[j];//即字符不匹配时回溯两层后对应的字符的下标
        }
        else
            j = Nextval[j]; //回溯
    }
}
int kmp(char text[], char pattern[]) //KMP算法,模式串、文本串
{
    int lent = strlen(text), lenp = strlen(pattern);
    int i = 0, j = 0;       //i、j均初始化为0
    getnextval(pattern, Nextval); //记得用Nextval数组
    while (i < lent)        //注意不是i<lent&&j<lenp
    {
        if (j == -1 || text[i] == pattern[j])
        {
            i++;
            j++;
        }
        else
            j = Nextval[j]; //回溯过程,i不变,j后退
    }
    if (j >= lenp)
        return i - lenp; //返回匹配模式下的首字母下标
    else
        return -1; //返回-1表示未匹配
}

最小表示法

  • 该算法通过两个指针不断向后移动,尝试比较每个循环同构串的大小,及时排除不可能的选项,当一个移动到结尾时,就考虑了所有可能的二元组(b[i],b[j]),从而得到了最小表示【O(n)】
  • 引理:b[i]表示从i开始的循环同构字符串,即b[i]=s [i ~ n]+s[1~i-1],若b[i],b[j]在k处不等,假设s[i+k]>s[j+k],则b[i],b[i+1]…b[i+k]都不是最小表示,因为它们最终也会经过s[i+k]>s[j+k]
int Get_min()
{
    int n = strlen(s);//字符串从下标0开始
    int i = 0, j = 1, k = 0, t;
    while (i < n && j < n && k < n) //表示从i开始k长度和从j开始k长度的字符串相同
    {
        t = s[(i + k) % n] - s[(j + k) % n]; //t用来计算相对应位置上那个字典序较大
        if (!t)
            k++; //字符相等的情况
        else
        {
            if (t > 0)
                i += k + 1; //i位置大,最大表示法: j += k+1
            else
                j += k + 1; //j位置大,最大表示法: i += k+1
            if (i == j)
                j++;
            k = 0;
        }
    }
    return min(i, j);
}

总结

QAQ菜🐕

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WTcrazy _

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值