Educational Codeforces Round 70

Educational Codeforces Round 70
E:
題意是給定 nn 個字符串 sis_i ,對於任意組合 si+sjs_i+s_j ,求所有組合在 tt 中的出現次數之和。
因爲 sis_isjs_j 是相連的,因此可以在 tt 上枚舉連接點,然後求出以連接點右邊第一個點爲起始點有多少個 ss,然後求出連接點左邊第一個點爲結束點有多少個 ss,然後將這兩個值相乘就是該連接點的貢獻了。
因爲將所有字符串翻轉後,左邊第一個點結束點有多少個 ss 個問題轉化爲右邊第一個點爲起始點有多少個 ss 的問題,因此我們只要求後一個問題即可。
可以將所有字符串按長度分類,長度小於 BB 的字符串放到字符串中,長度大於 BB 的字符串通過哈希或者kmp計算。將 BB 設爲 tt 的根號長度後整體的複雜度就變成了 O(len(t)1.5)O(len(t)^{1.5}) 了。
除此之外,這個問題也是可以通過AC自動機來求的,複雜度就線性了。AC自動機在構建fail指針時需要將 c[now]+=c[fail[now]]c[now]+=c[fail[now]] ,因爲如果匹配到了 nownow ,說明也匹配到了 fail[now]fail[now] 。然後直接將 tt 在AC自動機上掃,對於 t[i]t[i] ,對應的AC自動機上的狀態 c[now]c[now] 就是 ss 中所有爲 t[1..i]t[1..i] 的後綴的數量。這裏就和根號做法的kmp很像,因爲AC自動機就是多模kmp。

根號分塊做法:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+7;
const int B = 300;
using ll = long long;
string t;
int c[N], revc[N], nxt[N];
vector<string> ls, ss;
struct Trie {
    int nxt[N][26], tot, rt, cnt[N];
    void init() {
        tot = 0;
        rt = newnode();
    }
    int newnode() {
        ++tot;
        memset(nxt[tot], 0, sizeof(nxt[tot]));
        cnt[tot] = 0;
        return tot;
    }
    void insert(const string& s) {
        int now = rt;
        for(int i=0; i<s.length(); ++i) {
            int ch = s[i]-'a';
            if(nxt[now][ch]==0) nxt[now][ch]=newnode();
            now = nxt[now][ch];
        }
        ++cnt[now];
    }
    int query(const string& t, int start) {
        int res = 0;
        int now = rt;
        for(int i=start; i<t.length(); ++i) {
            int ch = t[i]-'a';
            now = nxt[now][ch];
            if(now==0) break;
            res += cnt[now];
        }
        return res;
    }
}trie;
void kmp_pre(const string &s) {
    int i,j;
    j=nxt[0]=-1;
    i=0;
    while(i<s.length()) {
        while(-1!=j&&s[i]!=s[j]) j=nxt[j];
        nxt[++i]=++j;
    }
}
void kmp(const string &s, int *c) {
    int i=0, j=0, ans=0;
    kmp_pre(s);
    while(i<t.length()) {
        while(-1!=j&&t[i]!=s[j]) j=nxt[j];
        ++i; ++j;
        if(j>=s.length()) {
            ++c[i-s.length()];
            j=nxt[j];
        }
    }
}
void cal(int *c) {
//    puts("cal");
    trie.init();
    for(string &s : ss) {
        trie.insert(s);
    }
    for(int i=0; i<t.length(); ++i) {
        c[i]=trie.query(t, i);
//        printf("c[%d]=%d\n", i, c[i]);
    }
    for(string &s : ls) {
        kmp(s, c);
    }
//    for(int i=0; i<t.length(); ++i) {
//        printf("c[%d]=%d\n", i, c[i]);
//    }
}
int main() {
    ios::sync_with_stdio(false);
    cin >> t;
    int n;
    cin >> n;
    for(int i=0; i<n; ++i) {
        string s;
        cin >> s;
        if(s.length()<B) ss.push_back(s);
        else ls.push_back(s);
    }
    cal(c);
    reverse(t.begin(), t.end());
    for(string &s : ls) reverse(s.begin(), s.end());
    for(string &s : ss) reverse(s.begin(), s.end());
    cal(revc);
    ll ans = 0;
    for(int i=1; i<t.length(); ++i) {
        ans += 1LL*c[i]*revc[t.length()-i];
//        printf("%d %d\n", i, t.length()-i);
    }
    cout << ans<<endl;
}



AC自動機做法:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+7;
using ll = long long;
string t;
int c[N], revc[N], nxt[N];
vector<string> ls;
struct Trie {
    int nxt[N][26], tot, rt, cnt[N], fail[N];
    void init() {
        tot = 0;
        rt = newnode();
    }
    int newnode() {
        ++tot;
        memset(nxt[tot], 0, sizeof(nxt[tot]));
        cnt[tot] = 0;
        return tot;
    }
    void insert(const string& s) {
        int now = rt;
        for(int i=0; i<s.length(); ++i) {
            int ch = s[i]-'a';
            if(nxt[now][ch]==0) nxt[now][ch]=newnode();
            now = nxt[now][ch];
        }
        ++cnt[now];
//        printf("now: %d, c: %d\n", now, cnt[now]);
    }
    void build()
    {
        queue<int> q;
        for(int i=0;i<26;++i)
        {
            if(nxt[rt][i]==0)
                nxt[rt][i]=rt;
            else
            {
                fail[nxt[rt][i]]=rt;
                q.push(nxt[rt][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            cnt[now] += cnt[fail[now]];
            for(int i=0;i<26;++i)
            {
                if(nxt[now][i]==0)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    void solve(int *c) {
        int now = rt;
        for(int i=0; i<t.length(); ++i) {
            int ch = t[i]-'a';
            now = nxt[now][ch];
            c[i] += cnt[now];
        }
    }
}trie;
void cal(int *c) {
//    puts("cal");
    trie.init();
    for(string &s : ls) trie.insert(s);
    trie.build();
//    for(int i=1; i<=trie.tot; i++) printf("cnt[%d]=%d\n", i, trie.cnt[i]);
    trie.solve(c);
//    for(int i=0; i<t.length(); ++i) {
//        printf("c[%d]=%d\n", i, c[i]);
//    }
}
int main() {
    ios::sync_with_stdio(false);
    cin >> t;
    int n;
    cin >> n;
    for(int i=0; i<n; ++i) {
        string s;
        cin >> s;
        ls.push_back(s);
    }
    cal(c);
    reverse(t.begin(), t.end());
    for(string &s : ls) reverse(s.begin(), s.end());
    cal(revc);
    ll ans = 0;
    for(int i=0; i+1<t.length(); ++i) {
        ans += 1LL*c[i]*revc[t.length()-i-2];
//        printf("%d %d\n", i, t.length()-i-2);
    }
    cout << ans<<endl;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章