1. 程式人生 > >codeforces856 B Similar Words 模型轉換 tree圖建圖+樹形dp

codeforces856 B Similar Words 模型轉換 tree圖建圖+樹形dp

B. Similar Words time limit per test2 seconds memory limit per test512 megabytes inputstandard input outputstandard output Let us call a non-empty sequence of lowercase English letters a word. Prefix of a word x is a word y that can be obtained from x by removing zero or more last letters of x.

Let us call two words similar, if one of them can be obtained from the other by removing its first letter.

You are given a set S of words. Find the maximal possible size of set of non-empty words X such that they satisfy the following:

each word of X is prefix of some word from S; X has no similar words. Input Input data contains multiple test cases. The first line of the input data contains an integer t — the number of test cases. The descriptions of test cases follow.

The first line of each description contains an integer n — the number of words in the set S (1 ≤ n ≤ 106). Each of the following n lines contains one non-empty word — elements of S. All words in S are different.

It is guaranteed that the total length of all words in one input data doesn’t exceed 106.

Output For each test case print one line that contains one integer m — the maximal number of words that X can contain.

Example input 2 3 aba baba aaab 2 aa a output 6 1

題意: 題意有點迷。 給出n個串,要求你從中選出一些字首 使得這些字首都不‘相似’ A與B相似 當且僅當 A(B) 去掉首字母后==B(A)

解題思路 題目給出的輸入和資料範圍很像AC自動機之類的題目。 想往這方面思考。 考慮建出tire圖後,任意一個節點都是一個字首,兩個點A,B不能同時存在的條件是 fail[A] = B 且deep[A]-1=deep[B] 然後再努(bai)力(du)一下 將不能同時取的點之間都連一條邊 模型就轉換成了 任意兩個相鄰點不能同時取到。 然後再努力思考一下 可以得出 按照上述建圖方法,建出來的不可能是圖,只有可能是樹或森林。 那就很好辦了,經典樹形dp模板題。 感覺很有意思。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 2e6 + 5;
const int maxn = 2e6 + 100;
const int SIGMA_SIZE = 26;
const int MAXNODE = 1000005;
const int MAXS = 1e6+10;
struct ACautomata {

    int ch[MAXNODE][SIGMA_SIZE];
    int f[MAXNODE];    // fail函式
    //int val[MAXNODE];  // 每個字串的結尾結點都有一個非0的val
    int last[MAXNODE]; // 輸出連結串列的下一個結點
    int deep[MAXNODE];
    int sz;
    //int d[MAXNODE];
    void init() {
        sz = 1;
        memset(ch[0], 0, sizeof (ch[0]) );
        //memset(d, 0, sizeof(d));
    }

    // 字元c的編號
    inline int idx (char c) {
        return c - 'a';
    }

    // 插入字串。v必須非0
    void insert (char *s) {
        int u = 0, n = strlen (s),dlen=0;
        for (int i = 0; i < n; i++) {
            int c = idx (s[i]);
            dlen++;
             //printf("%c", s[i]);
            if (!ch[u][c]) {
                memset (ch[sz], 0, sizeof (ch[sz]) );
                deep[sz]=dlen;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
            //printf("%d", u);
            //puts("");
        }
    }
    // 計算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]];
            }
        }
    }
    long long dp[2][MAXS];
    bool vis[MAXS];
    class Edge{
        public:
        int v,nxt;
    };
    Edge edge[MAXS<<2];
    int tot;
    int head[MAXS];
    void add(int u,int v){
        edge[tot].nxt=head[u];
        edge[tot].v=v;
        head[u]=tot;
        tot++;
    }
    void build_tree(){
        for(int i=0;i<=sz;i++){
            vis[i]=0;
            head[i]=-1;
        }
        tot=0;
        for(int i=1;i<sz;i++){
            int fa = f[i];
            //cout<<fa<<","<<i<<endl;
            if((deep[fa]==deep[i]-1) && deep[fa]!=0){
                add(fa,i);
                add(i,fa);
                //cout<<i<<"->"<<fa<<endl;
            }
        }
    }
    void dfs(int u,int pre){
        vis[u]=1;
        dp[1][u]=1;
        dp[0][u]=0;
        long long sum =0 ;
        for(int i=head[u];i!=-1;i=edge[i].nxt){
            int v= edge[i].v;
            if(v==pre) continue;
            dfs(v,u);
            dp[1][u]+=dp[0][v];
            dp[0][u]+=max(dp[0][v],dp[1][v]);
        }
    }
    void solve(){
        long long ans =0;
        for(int i=1;i<sz;i++){
            if(!vis[i]){
                dfs(i,-1);
                ans+=max(dp[0][i],dp[1][i]);
            }
        }
        printf("%lld\n",ans);
    }
} ac;

char s[MAXN];



int main() {
    int n;
    int T;
    cin>>T;
    while(T--) {
        cin >> n;
        ac.init();
        for (int i = 1; i <= n; i++) {
            scanf ("%s", s);
            ac.insert (s);
        }
        ac.getFail();
        ac.build_tree();
        ac.solve();
    }
    return 0;
}