[树形dp][trie]cf856B Similar Words

12 篇文章 0 订阅
11 篇文章 0 订阅

Description

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(1n106 ). 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 <script type="math/tex" id="MathJax-Element-7">m</script> — the maximal number of words that X can contain.

Sample Input

2
3
aba
baba
aaab
2
aa
a

Sample Output

6
1

在一棵树上,相邻的点不能同时取,问最多取多少个点。
dp[i][0]代表不取本点,子树的答案,dp[i][1]代表取,树形dp采用dfs实现即可。

树的构造过程为similiar words之间连边。至于为什么形成的是树,可以采用反证法证明没有偶环(最长的前缀只能连接一个similar word),而奇环显然不存在。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 5;
const int maxm = 26;

int tot, n;
int ch[maxn][maxm];

int newNode()
{
    memset(ch[tot], 0, sizeof ch[tot]);
    return tot++;
}

char s[maxn];

void add(char *s)
{
    int len = strlen(s);
    int cur = 0;
    for(int i = 0; i < len; ++ i)
    {
        int v = s[i] - 'a';
        if(ch[cur][v] == 0) ch[cur][v] = newNode();
        cur = ch[cur][v];
    }
}

string str[maxn];
vector<int> G[maxn];

void buildGraph(const string &s)
{
    int a = 0;
    int b = ch[0][s[0]-'a'];
    for(int i = 1; i < s.size(); ++ i)
    {
        int v = s[i] - 'a';
        a = ch[a][v];
        b = ch[b][v];
        if(a == 0 || b == 0) return;
        G[a].push_back(b);
        G[b].push_back(a);
    }
}

int f[maxn][2];
bool vis[maxn];

void dfs(int u)
{
    vis[u] = true;
    f[u][0] = 0;
    f[u][1] = 1;
    for(auto v : G[u])
    {
        if(vis[v]) continue;
        dfs(v);
        f[u][0] += max(f[v][0], f[v][1]);
        f[u][1] += f[v][0];
    }
}

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        tot = 1;
        cin >> n;
        memset(ch[0], 0, sizeof ch[0]);
        for(int i = 0; i < n; ++ i)
        {
            scanf("%s", s);
            add(s);
            str[i] = s;
        }

        for(int i = 1; i < tot; ++ i) G[i].clear();
        for(int i = 0; i < n; ++ i) buildGraph(str[i]);
        for(int i = 1; i <= tot; ++ i) vis[i] = false;

        int res = 0;
        for(int i = 1; i < tot; ++ i)
        {
            if(!vis[i])
            {
                dfs(i);
                res += max(f[i][0], f[i][1]);
            }
        }
        cout << res << endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值