CF 963D - Frequency of String AC自动机 均摊复杂度

题意:

给你一个串和q次询问。

每次询问给你一个k和一个字符串qi,问你原串当中包含这个字符串k次的最短子串长度是多少。

每次询问的子串不同。

\sum |qi| <= 1e5.

题解:

试想,如果对于每个查询串i每我们知道了他在原串中 出现的位置集合Ri,那么这个串我们可以通过|Ri|的时间找到答案。

对于所有的串,要找到他的Ri,其实只要把他们插入AC自动机里面,然后用原串进行匹配即可。

现在的问题是,复杂度是多少呢?

总复杂度其实就是\sum |Ri|。但是怎么分析他的大小呢?

对于原串每一个点,他可以作为多少个串的结尾呢?

注意到,询问的串长度的总数\sqrt{\sum |qi|}的,为什么呢?最坏情况所有询问的子串长度不同,那么1+2+3+...+x <= \sum |qi|,可知x < \sqrt{\sum |qi|}

又由于询问的串不相同,所以对于每个点,只有可能作为\sqrt{\sum |qi|}个串的结尾。

所以\sum |Ri|  是|S| * \sqrt{\sum |qi|} 的。

n\sqrt{n}的复杂度。

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <bitset>
#include <map>
#include <vector>
#include <stack>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <cmath>
#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif

#define chmax(x,y) x=max(x,y)
#define chmin(x,y) x=min(x,y)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> pii;

const ll MOD = 1e9 + 7;
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 1e5 + 5;

const int SIGMA_SIZE = 26;
const int MAXNODE = 100555;
const int MAXS = 150 + 10;

vector<int> pos[MAXN];

struct ACautomata {


    int ch[MAXNODE][SIGMA_SIZE];
    int f[MAXNODE];    // fail函数
    int val[MAXNODE];  // 每个字符串的结尾结点都有一个非0的val
    int last[MAXNODE]; // 输出链表的下一个结点
    //int cnt[MAXS];
    int sz;

    void init() {
        sz = 1;
        memset(ch[0], 0, sizeof(ch[0]));
        // memset(cnt, 0, sizeof(cnt));
    }

    // 字符c的编号
    inline int idx(char c) {
        return c - 'a';
    }

    // 插入字符串。v必须非0
    void insert(string & s, int v) {
        int u = 0, n = s.size();
        for(int i = 0; i < n; i++) {
            int c = idx(s[i]);
            if(!ch[u][c]) {
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
        }
        val[u] = v;
    }

    // 递归打印匹配文本串str[i]结尾的后缀,以结点j结尾的所有字符串
    void print(int i,int j) {
        if(j) {
            pos[val[j]].pb(i);
            print(i,last[j]);
        }
    }

    // 在T中找模板
    int find(char* T) {
        int n = strlen(T);
        int j = 0; // 当前结点编号,初始为根结点
        for(int i = 0; i < n; i++) { // 文本串当前指针
            int c = idx(T[i]);
            j = ch[j][c];
            if(val[j]) print(i,j);
            else if(last[j]) print(i,last[j]); // 找到了!
        }
    }

    // 计算fail函数
    void getFail() {
        queue<int> q;
        f[0] = 0;
        // 初始化队列
        for(int c = 0; c < SIGMA_SIZE; c++) {
            int u = ch[0][c];
            if(u) {
                f[u] = 0;
                q.push(u);
                last[u] = 0;
            }
        }
        // 按BFS顺序计算fail
        while(!q.empty()) {
            int r = q.front();
            q.pop();
            for(int c = 0; c < SIGMA_SIZE; c++) {
                int u = ch[r][c];
                if(!u) {
                    ch[r][c]=ch[f[r]][c];
                    continue;
                }
                q.push(u);
                int v = f[r];
                while(v && !ch[v][c]) v = f[v];
                f[u] = ch[v][c];
                last[u] = val[f[u]] ? f[u] : last[f[u]];
            }
        }
    }

} ac;

string s[MAXN];
int k[MAXN];
char str[MAXN];
int ans[MAXN];

int main() {
#ifdef LOCAL
    freopen ("input.txt", "r", stdin);
#endif
    int n;
    ac.init();
    scanf("%s %d", str + 1, &n);
    for(int i = 1; i <= n; i++) {
        cin >> k[i] >> s[i];
        ac.insert(s[i], i);
    }
    ac.getFail();
    ac.find(str + 1);
    memset(ans, 0x3f, sizeof(ans));
    for(int i = 1; i <= n; i++) {
        for(int j =0; j + k[i] <= pos[i].size(); j++) {
            ans[i] = min(ans[i], pos[i][j + k[i] - 1] - pos[i][j]);
        }
        if(ans[i] == INF) puts("-1");
        else printf("%d\n", ans[i] + s[i].size());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/c6376315qqso/article/details/82634551
今日推荐