多校赛第五场1006 string matching(扩展KMP)

扩展KMP求的是对于原串S1的每一个后缀子串与模式串S2的最长公共前缀

对KMP算法进行了优化,下面是模板

int Next[maxn],extend[maxn];
char s[1000050],t[1000050];

void getNext(char* str){
    int i=0,j,pos,len=strlen(str);
    Next[0]=len;
    while(str[i]==str[i+1]&&i+1<len)i++;Next[1]=i;
    pos=1;
    for(int i=2;i<len;i++){
        if(Next[i-pos]+i<Next[pos]+pos){
            Next[i]=Next[i-pos];
        }
        else{
            j=Next[pos]+pos-i;
            if(j<0)j=0;//如果i比最远的的小,说明得从头开始。
            while(i+j<len&&str[j]==str[j+i])j++;Next[i]=j;
            pos=i;
        }
    }
}

void Exkmp(char* s,char* t){
    int i=0,j,pos,l1=strlen(s),l2=strlen(t);
    getNext(t);
    while(s[i]==t[i]&&i<l1&&i<l2)i++;extend[0]=i;
    pos=0;
    for(int i=1;i<l1;i++){
        if(Next[i-pos]+i<extend[pos]+pos)
            extend[i]=Next[i-pos];
        else{
            j=extend[pos]+pos-i;
            if(j<0)j=0;
            while(i+j<l1&&j<l2&&s[i+j]==t[j])j++;extend[i]=j;
            pos=i;
        }
    }
}

对于string matching这道题,直接套模板然后将str和自己做ekmp,最后将extend数组的数字加起来就可以得到答案

(注意要long long)

下面是ac代码

#include<stdio.h>
#include<string.h>
const int maxn = 1000005; //字符串长度最大值
long long next[maxn], ex[maxn]; //ex数组即为extend数组
char s[maxn];
//预处理计算next数组
void GETNEXT(char *str)
{
    int i = 0, j, po, len = strlen(str);
    next[0] = len; //初始化next[0],因为从0开头就可以匹配整个T,故为len   /*与EXKMP代码不同处:无这句话*/
    while(str[i] == str[i + 1] && i + 1 < len) //计算next[1]  /*与EXKMP代码不同处:比较s1和s2*/
        i++;
    next[1] = i;/*与EXKMP代码不同处:ex[0]=i*/
    po = 1; //初始化po的位置  /*与EXKMP代码不同处:po = 0*/
    for(i = 2; i < len; i++)/*与EXKMP代码不同处:i从1开始*/
    {
        if(next[i - po] + i < next[po] + po) //第一种情况,可以直接得到next[i]的值  /*与EXKMP代码不同处:next[i-po]+i<ex[po]+po*/
            next[i] = next[i - po];/*与EXKMP代码不同处:ex[i]=next[i-po];*/
        else//第二种情况,要继续匹配才能得到next[i]的值
        {
            j = next[po] + po - i;/*与EXKMP代码不同处:j=ex[po]+po-i;*/
            if(j < 0)j = 0; //如果i>po+next[po],则要从头开始匹配
            while(i + j < len && str[j] == str[j + i]) //计算next[i]  /*与EXKMP代码不同处:i+j<len&&j<l2&&s1[j+i]==s2[j]*/
                j++;
            next[i] = j;/*与EXKMP代码不同处:ex[i]=j;*/
            po = i; //更新po的位置
        }
    }
}
//计算extend数组
void EXKMP(char *s1, char *s2)
{
    int i = 0, j, po, len = strlen(s1), l2 = strlen(s2);
    GETNEXT(s2);//计算子串的next数组
    while(s1[i] == s2[i] && i < l2 && i < len) //计算ex[0]
        i++;
    ex[0] = i;
    po = 0; //初始化po的位置
    for(i = 1; i < len; i++)
    {
        if(next[i - po] + i < ex[po] + po) //第一种情况,直接可以得到ex[i]的值
            ex[i] = next[i - po];
        else//第二种情况,要继续匹配才能得到ex[i]的值
        {
            j = ex[po] + po - i;
            if(j < 0)j = 0; //如果i>ex[po]+po则要从头开始匹配
            while(i + j < len && j < l2 && s1[j + i] == s2[j]) //计算ex[i]
                j++;
            ex[i] = j;
            po = i; //更新po的位置
        }
    }
}
int main()
{
    int T;
    while(~scanf("%d",&T))
    {
        while(T--)
        {
            long long sum = 0;
            scanf("%s",s);
            EXKMP(s,s);
            for(int i=1;i<strlen(s);i++)
            {
                if(i + ex[i] == strlen(s))
                {
                    sum += ex[i];
                }
                else
                    sum += ex[i]+1;
            }
            printf("%lld\n",sum);        
        }    
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值