[NOIP模拟] 字符串

题目大意:

    给出 n 个串(len <= 100), 再给出一个主串(len <= 100000), 给出 Q 次询问, 每次包括一个整数和一个字母,求主串在该整数位置将字母改为该字母后,n个串再主串中出现的次数。

输入简述 :

    输入N <= 1000, Q <= 200000, N 个串,一个主串, Q次询问的整数和字母。

输出简述 :

    最初值 + Q次询问的答案。

sample input :

3 5
ab
bc
abc
acbcb
2 b
3 c
4 a
1 b
3 a

sample ouput :

1
2
3
4
2
1


题解 :

    提说这是 NOI 测试的签到题, 看来还是我太弱了。
    我们考虑每次修改对答案的影响, 每次修改我们只会影响 pos - len + 1 到 pos + len - 1 的值, len 为非主串的字符串长度, pos为修改的位置, 于是我们只需要求出在这段区间的值就可以了, 那我们这里就会跑两边 AC 自动机,由于 len 不超过 100, 但我们会多次跳 fail, 所以这里可能会超, 于是我们考虑优化,我们将每个 AC 自动机上的点, 把他们所有的 fail 指针所跳的点的 cnt 都加起来,在预处理完成,这样我们就不用跳 fail 了。




代码 :

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <ctime>
#include <map>
#include <vector>
#include <queue>
#define clr(a) memset(a, 0, sizeof(a))
using namespace std;

inline int read() {
    int i = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-') f = -1; ch = getchar();
    }
    while(isdigit(ch)) {
        i = (i << 3) + (i << 1) + ch - '0'; ch = getchar();
    }
    return i * f;
}

const int MAXN = 1e5 + 5;
int mx;
char s[MAXN];

struct AC {
    int tot;
    struct point {
        int trans[26], cnt, fail;
        void clear() {
            clr(trans); cnt = fail = 0;
        }
    } tr[MAXN * 10];
    inline void init() {
        for(int i = 0; i < 26; ++i) tr[0].trans[i] = 1;
        tr[tot = 1].clear();
    }

    inline void insert(char * s) {
        int u = 1;
        int len = strlen(s + 1); mx = max(mx, len);
        for(int i = 1; i <= len; ++i) {
            if(!tr[u].trans[s[i] - 'a']) tr[tr[u].trans[s[i] - 'a'] = ++tot].clear();
            u = tr[u].trans[s[i] - 'a']; 
        }
        tr[u].cnt++;
    }

    inline void buildfail() {
        queue<int> q;
        q.push(1);
        while(!q.empty()) {
            int u = q.front(); q.pop();
            for(int i = 0; i < 26; ++i) {
                int v = tr[u].fail;
                while(!tr[v].trans[i]) v = tr[v].fail;
                v = tr[v].trans[i]; int w = tr[u].trans[i];
                if(w) tr[w].fail = v, q.push(w), tr[w].cnt += tr[v].cnt;
                else tr[u].trans[i] = v;
            }
        }
    }

    inline int solve(char *s, int l, int r) {
        int now = 1, ans = 0;
        for(int i = l; i <= r; ++i) {
            now = tr[now].trans[s[i] - 'a'];
            ans += tr[now].cnt;
        }
        return ans;
    }
}acam;

int main() {
    //freopen("1.in", "r", stdin);
    int n = read(), m = read();
    acam.init();
    for(int i = 1; i <= n; ++i) scanf("%s", s + 1), acam.insert(s); 
    acam.buildfail();
    scanf("%s", s + 1); int len = strlen(s + 1);
    int ans = acam.solve(s, 1, len);
    printf("%d\n", ans);
    while(m--) {
        int pos = read(); char ch = getchar(); 
        int ans1 = acam.solve(s, max(1, pos - mx + 1), min(len, pos + mx - 1));
        s[pos] = ch; 
        int ans2 = acam.solve(s, max(1, pos - mx + 1), min(len, pos + mx - 1));
        ans += ans2 - ans1;
        cout<<ans<<'\n';
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值