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


题意:

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

题解:

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

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

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

那么原先,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、付费专栏及课程。

余额充值