Educational Codeforces Round 134 (Rated for Div. 2) E. Prefix Function Queries(KMP优化)

本文介绍了一种针对KMP算法的优化方法,通过预处理字符串并构建特殊数组来提高匹配效率。这种方法尤其适用于需要频繁查询的情况,能显著减少运行时间。


题意:

给定一个sss串,然后qqq个询问,每个询问给个ttt串(保证ttt串长度<=10)然后把ttt串接到sss串后面,然后求t中的字符的nextnextnext数组(每个询问独立,也就是说每个询问后sss串还是原本的sss串,不会变成s+ts+ts+t

题解:

先预处理sss串的nextnextnext数组,然后对于每个询问对结尾ttt串跑nextnextnext数组。这是显然的做法,然后TLE了。只要ttt串的数据是正好要跳sss串整个长串的话,每次就是跑1e61e61e6的长度了,这样的话极限时间复杂度是O(q∗∣s∣)O(q*|s|)O(qs),所以会TLE。

然后就是想怎么优化这个kmpkmpkmpkmpkmpkmp的本质是一颗树(如果每个next[i]next[i]next[i]iii建边,那么会形成一颗以000为根的树),然后kmpkmpkmp匹配的过程就是一直往父节点跳的过程。那么如果能够跳一次就直接跳到应该匹配的位置就好了,而不是一直跳,然后边跳边判断。

所以引入一个ch[N][26]ch[N][26]ch[N][26]数组。ch[i][j]ch[i][j]ch[i][j]表示iii这个点若匹配字符jjj要会匹配到的位置

那么原先,next[i]匹配过程为:

void get_next(string &s) {
    nxt[1] = 0;//下标从1开始
    for (int i = 2; i < s.length(); i++) {
        nxt[i] = nxt[i - 1];
        while (nxt[i] && s[i] != s[nxt[i] + 1])
            nxt[i] = nxt[nxt[i]];
        if (s[nxt[i] + 1] == s[i])nxt[i]++;
    }
}

就变为了

nxt[1] = 0;ch[0][s[1] - 'a'] = 1;
for (int i = 2; i <= n; i++) {
    nxt[i] = ch[nxt[i - 1]][s[i] - 'a'];
    ch[i - 1][s[i] - 'a'] = i;
    for (int j = 'a'; j <= 'z'; j++) {
        if (j == s[i])continue;
        ch[i - 1][j - 'a'] = ch[nxt[i - 1]][j - 'a'];
    }
}

然后附上严格鸽的题解:严格鸽的题解

.下面是我的关键代码

​
void solve(){
    cin>>s+1;
    n=strlen(s+1);
    nxt[1]=0;ch[0][s[1]-'a']=1;
    for(int i=2;i<=n;i++){
        int c=s[i]-'a';
        nxt[i]=ch[nxt[i-1]][c];
        ch[i-1][c]=i;
        for(int j='a';j<='z';j++){
            if(j==s[i]) continue;
            int c=j-'a';
            ch[i-1][c]=ch[nxt[i-1]][c];
        }
    }
    int m;cin>>m;
    while(m--){
        cin>>t+1;
        int len=strlen(t+1)+n;
        for(int i=n+1;i<=len;i++) s[i]=t[i-n];
        for(int i=n+1;i<=len;i++){
            int c=s[i]-'a';
            nxt[i]=ch[nxt[i-1]][c];
            ch[i-1][c]=i;
            for(int j='a';j<='z';j++){
                if(j==s[i]) continue;
                int c=j-'a';
                ch[i-1][c]=ch[nxt[i-1]][c];
            }
            cout<<nxt[i]<<' ';
        }cout<<endl;
    }
}

​
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值