poj 4025 Dictionary Size

        Trie。反思:一直想的是直接把答案算出来,然后就各种wa。其实反着想的话才是正解。。。就是先把所有情况都算出来,然后减去重复的情况。这个样例

        3

        ab
        ac

        bc

就是有组成新串有重复情况的样例。对付这种题,很显然我们会想到要建两个Trie,一个存前缀,一个存后缀。然后两个Trie的节点数相乘就是总数了。关键是去重。考虑一下节点数相乘求新串个数的原理(假如没有重复。)我们是在前缀trie上一个字符到根这一段字符串作为新串的前半段,在后缀trie上一个字符到根这一段字符串作为新串的后半段(串为反串)。这样的话,因为两部分都不能为空,我们需要统计每个字母出现的次数。如我前面写的那个样例:

                                                

                                                             

                                                                           前缀Trie                                                                                               后缀Trie

          这样的话我们取前缀ab,后缀c   为一个串abc。然后前缀 a ,后缀 bc  又组成了相同串 abc。这样的话就是重复的位置了,当然,这也是解决问题关键。为什么会出现这种情况呢?是因为两边都有字母b!导致abc,这种串算了两次。于是结果应该减去这种重复。统计两边相同字母出现次数,然后用总数减去他们的乘积就好了。对于与根节点相连的字母我们是不用统计的,因为我们是两边都取串,这样的话原串对应的串就不会被重复进去,原串只算了一遍,所以不用统计。然后就是如果输入的有长度为 1 的串的话要单独考虑自身串,因为这种串是不会出现在两前面所求的情况中的。

#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <climits>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <cctype>
#include <queue>
#include <cmath>
#include <set>
#include <map>
#define CLR(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long LL;

const int MAX_NODE = 10010 * 40;
const int INF = 0x3f3f3f3f;
const int CHILD_NUM = 26;
const int N = 44;

class Trie
{
private:
    int chd[MAX_NODE][CHILD_NUM];
    LL cnt[CHILD_NUM];
    int ID[128];
    int sz;
public:
    void Initialize() {
        for(int i = 0; i < 26; i ++)
            ID[i + 'a'] = i;
    }
    void Reset() {
        CLR(chd[0] , 0);sz = 1;
        CLR(cnt, 0);
    }
    void Insert(char *a)
    {
        int p = 0;
        for ( ; *a ; a ++) {
            int c = ID[*a];
            if (!chd[p][c]) {
                CLR(chd[sz] , 0);
                if(p != 0) cnt[c] ++;
                chd[p][c] = sz ++;
            }
            p = chd[p][c];
        }
    }
    LL CNT(int i) { return cnt[i]; }
    int SZ() { return sz - 1;}
} Tr1, Tr2;

char a[N], b[N];

int main()
{
    //freopen("input.txt", "r", stdin);
    int n, len;bool cnt[27];LL ans;
    Tr1.Initialize();
    Tr2.Initialize();
    while(scanf("%d", &n) != EOF)
    {
        Tr1.Reset();Tr2.Reset();CLR(cnt, 0);
        for(int i = 0; i < n; i ++)
        {
            scanf("%s", a);
            len = strlen(a);
            if(len == 1) cnt[a[0] - 'a'] = 1;
            for(int j = 0; j < len; j ++)
            {
                b[len - j - 1] = a[j];
            }
            b[len] = '\0';
            Tr1.Insert(a);
            Tr2.Insert(b);
        }
        ans = LL(Tr1.SZ()) * Tr2.SZ();
        for(int i = 0; i < 26; i ++)
        {
            ans -= Tr1.CNT(i) * Tr2.CNT(i);
            if(cnt[i]) ans ++;
        }
        printf("%I64d\n", ans);
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值