1. 程式人生 > 實用技巧 >[Usaco10Dec] Threatening Letter G - 字尾自動機,貪心

[Usaco10Dec] Threatening Letter G - 字尾自動機,貪心

Description

給你一個長度為 \(n\) 的串 \(s_1\),再給你一個長度為 \(m\) 的串 \(s_2\),問需要至少多少個 \(s_1\) 的子串才可以拼成 \(s_2\)

Solution

\(s_1\) 建出 SAM,把 \(s_2\) 放到上面貪心地跑,如果能匹配就接著沿著 \(trans\) 邊走,不匹配則回到根結點,並且 \(ans+1\)

#include <bits/stdc++.h>
using namespace std;
const int N = 2000005;
int ans=1;
struct SAM {
    int len[N], ch[N][26], fa[N], ind, last;
    int t[N], a[N], cnt[N], f[N];
    SAM() { ind = last = 1; }
    inline void extend(int id) {
        int cur = (++ ind), p;
        len[cur] = len[last] + 1;
        cnt[cur] = 1;
        for (p = last; p && !ch[p][id]; p = fa[p]) ch[p][id] = cur;
        if (!p) fa[cur] = 1;
        else {
            int q = ch[p][id];
            if (len[q] == len[p] + 1) fa[cur] = q;
            else {
                int tmp = (++ ind);
                len[tmp] = len[p] + 1;
                for(int i=0;i<26;i++) ch[tmp][i] = ch[q][i];
                fa[tmp] = fa[q];
                for (; p && ch[p][id] == q; p = fa[p]) ch[p][id] = tmp;
                fa[cur] = fa[q] = tmp;
            }
        }
        last = cur;
    }
    int p=1;
    void go(int id)
    {
        if(ch[p][id]) p=ch[p][id];
        else p=ch[1][id], ++ans;
    }
} sam;

int main() {
    ios::sync_with_stdio(false);
    string str,tmp;
    int n,m;
    cin>>n>>m;
    while(str.length()<n)
    {
        cin>>tmp;
        str+=tmp;
    }
    for(int i=0;i<n;i++) sam.extend(str[i]-'A');
    str="";
    while(str.length()<m)
    {
        cin>>tmp;
        str+=tmp;
    }
    for(int i=0;i<m;i++) sam.go(str[i]-'A');
    cout<<ans<<endl;
}