UVAlive 5792 Diccionário Portuñol (Trie)

转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove

题目:有两个字符串集合,从第一个集合中取某个串的非空前缀,从第二个集合中取某个串的非空后缀,拼接成一个串,问有多少个不同的新串。

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3803

大概是N年前训练的题。。。然后不会做,就没管了

最近波波好同学问了御坂姐姐,然后就会做了,其实很水。。。

为何这么弱。。。。

首先把第一个集合中的串加入到Trie中,然后就可以枚举前缀了,然后就是统计后缀,麻烦的是可能出现重复的。

当我们按长度枚举前缀的时候,可以考虑如果后缀的前缀出现在前缀的后继中,那便不用考虑,后面总会统计到。

这样的话,其实只要比较第一个字母。

如果当前结点P表示一个前缀串,那么如果p -> next[i]为空,说明前缀中不含字母i为后继,那么我们就可以统计以i开头的后缀了。否则总能在p -> next[i]为前缀中统计到。

有一点需要注意的是,如果 p -> next[i]便是我们最终的串,即以p 为前缀时,后缀只有一个字母的话,那就不能把这一个字母拉入到前缀中,因为后缀是非空的。所以再处理下end[i],表示是否存在以单个字母i为后缀的。

至于统计以i开头的不重复后缀的话,可以反向加入到Trie中统计。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
typedef long long LL;
const int M = 100005;
const int N = 1005;
int n , m , cnt[26] , end[26];
char a[N][N] , b[N][N];
LL ans = 0;
struct Trie {
    int tot;
    struct trie {
        trie *next[26];
    }s[M] , *root;
    trie *newnode () {
        trie *p = &s[tot ++];
        memset (p -> next , 0 , sizeof(p -> next));
        return p;
    }
    void init () {
        memset (cnt , 0 , sizeof(cnt));
        tot = 0;
        root = newnode ();
    }
    void insert (char *s) {
        trie *p = root;
        for (int i = 0 ; s[i] ; i ++) {
            int c = s[i] - 'a';
            if (p -> next[c] == NULL) {
                p -> next[c] = newnode ();
            }
            p = p -> next[c];
        }
    }
    void cal (trie *p) {
        for (int i = 0 ; i < 26 ; i ++) {
            if (p -> next[i]) {
                cnt[i] ++;
                cal (p -> next[i]);
            }
        }
    }
    void gao (trie *p) {
        for (int i = 0 ; i < 26 ; i ++) {
            if (p -> next[i] == NULL) {
                if (p != root) ans += cnt[i];
            }
            else {
                if (p != root && end[i]) ans ++;
                gao (p -> next[i]);
            }
        }
    }
}suffix , prefix;
int main () {
    while (scanf ("%d %d" , &n , &m) != EOF && n + m) {
        prefix.init ();suffix.init ();
        ans = 0;
        memset (cnt , 0 , sizeof (cnt));
        memset (end , 0 , sizeof (end));
        for (int i = 0 ; i < n ; i ++) {
            scanf ("%s" , a[i]);
            prefix.insert (a[i]);
        }
        for (int i = 0 ; i < m ; i ++) {
            scanf ("%s" , b[i]);
            reverse (b[i] , b[i] + strlen(b[i]));
            end[b[i][0] - 'a'] = 1;
            suffix.insert (b[i]);
        }
        suffix.cal (suffix.root);
        prefix.gao (prefix.root);
        printf ("%lld\n" , ans);
    }
    return 0;
}


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值