Codeforces 235C Cyclical Quest
先处理循环的问题。
令 \(n = |t|, T = t + t\),那么对于 \(n \le i < 2n\) 的 \(i\),\([i - n + 1, i]\) 就对应着 \(t\) 循环得到的串。
对于一个串 \(t\) 在 \(s\) 的出现次数是好做的。
建出 \(s\) 的 \(\text{SAM}\),统计 \(\text{SAM}\) 上每个节点 \(u\) 对应的 \(|endpos_u|\)。
这个可以得到 \(fail_u\) 后连边 \(fail_u\to u\),\(u\) 节点子树里的叶子节点个数即为 \(|endpos_u|\)。
然后依照 \(t\) 往后不断加字符(走不了则无解),最后走到的节点 \(u\) 就是 \(t\) 在 \(\text{SAM}\) 对应的节点,\(|endpos_u|\) 即为答案。
考虑类比这个方法来求出 \(T\) 的答案。
因为答案需要的是 \([i - n + 1, i]\),所以维护到 \(i\) 时满足 \(t_{i - len + 1, i}\) 在 \(s\) 中出现过的 \(len_{\max}\) 和对应在 \(\text{SAM}\) 上的节点 \(p\)。
令 \(l = len_{\max}\),如果 \(l\ge n\),则考虑一直跳 \(fail_p\) 直到 \(p\) 对应的 \(len\) 的区间包含了 \(n\),那么 \(p\) 就相当于包含了 \(t_{i - n + 1, i}\),\(|endpos_p|\) 即为答案。
为了保证复杂度正确,若 \(l\ge n\),直接把 \(p\) 跳上去即可无需撤销,能保证这对答案是无影响的,因为跳 \(fail\) 相当于删去前面的字符,对后面的字符并无影响。
对于去重。
考虑如果两个串相同,则两个串对应的 \(p\) 肯定相同。
所以只需要让每个节点 \(p\) 最多贡献一次答案就行了,可以直接在 \(p\) 这里打个标记。
时间复杂度 \(O(|S| + \sum |x_i|)\)。
#include<bits/stdc++.h>
const int maxn = 1e6 + 10;
namespace SAM {
    const int maxp = maxn * 2;
    int fail[maxp], len[maxp], ch[maxp][26], cnt[maxp];
    int lst, tot;
    inline void insert(int c) {
        int p = lst, cur = ++tot;
        lst = cur;
        len[cur] = len[p] + 1, cnt[cur] = 1;
        while (p && ! ch[p][c]) ch[p][c] = cur, p = fail[p];
        if (! p) fail[cur] = 1;
        else {
            int q = ch[p][c];
            if (len[p] + 1 == len[q]) fail[cur] = q;
            else {
                int nq = ++tot;
                fail[nq] = fail[q], len[nq] = len[p] + 1, memcpy(ch[nq], ch[q], sizeof(ch[nq]));
                fail[q] = fail[cur] = nq;
                while (p && ch[p][c] == q) ch[p][c] = nq, p = fail[p];
            }
        }
    }
    std::vector<int> son[maxp];
    void dfs(int u) {
        for (int v : son[u]) dfs(v), cnt[u] += cnt[v];
    }
    inline void build(char *s) {
        int n = strlen(s + 1);
        lst = tot = 1;
        for (int i = 1; i <= n; i++) insert(s[i] - 'a');
        for (int i = 2; i <= tot; i++) son[fail[i]].push_back(i);
        dfs(1);
    }
    int tag[maxp];
}
using SAM::fail, SAM::len, SAM::cnt, SAM::ch, SAM::tag;
char s[maxn * 2];
int main() {
    scanf("%s", s + 1);
    SAM::build(s);
    int Q; scanf("%d", &Q);
    for (int T = 1; T <= Q; T++) {
        scanf("%s", s + 1);
        int n = strlen(s + 1);
        for (int i = 1; i <= n; i++) s[n + i] = s[i];
        int p = 1, l = 0;
        long long ans = 0;
        for (int i = 1, c; i <= (n << 1); i++) {
            c = s[i] - 'a';
            while (p && ! ch[p][c]) p = fail[p], l = len[p];
            if (! p) p = 1, l = 0;
            else p = ch[p][c], l++;
            if (l >= n) {
                while (len[fail[p]] >= n) p = fail[p];
                tag[p] != T && (tag[p] = T, ans += cnt[p]);
                l = n;
            }
        }
        printf("%lld\n", ans);
    }
    return 0;
}
                    
                
                
            
        
浙公网安备 33010602011771号