UVALive 5913 Dictionary Size 解题报告

题目地址:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3924

题目描述:有n个字符串每个字符串的长度: 1 - 40. 现在希望组成新的字符串,新的字符串是原字符串的前缀和后缀拼接而成,要求你求出能够组成多少种新的字符串。

解题思路:我们可以用trie树很容易的求出这n个字符串有多少个不同的前缀和有多少个不同的后缀。对于每一种前缀和后缀都能够拼接成一个字符串。但是这样计算的存在重复计算的问题。例如 前缀Sf = aaa,和后缀Sb = aaa,那么对于拼接成字符串aaa则有Sf(0, 0) + Sb(1, 2)或者Sf(0, 1) + Sb(2, 2)等等的拼接方法。

在讲去重之前我们先来证明一个推论:


对于所有以字符x结尾的前缀Sf,和以x为开头的后缀Sb。对于每一对的Sf和Sb我们都重复计算了一次。

证明:假设:Sf = f1f2f3...X , Sb = X...b3b2b1. 对于字符串f1f2f3....X.....b3b2b1 我们重复计算了一次。


对于每一个我们重复计算的字符串S,我们都可以人认为是以字符x结尾的前缀Sf和以x为开头的后缀Sb所造成的。

证明:假设:S = s1s2s3.....sn, 假设SFa = Fa1Fa2Fa3......Faka 和 SBa = Ban-ka......Ba1 能拼接成S

                                                               SFb = Fb1Fb2Fb3......Fbkb 和 SBb = Bbn-kb......Bb1 能拼接成S

                                                               并且ka < kb. 易证Fa1Fa2Fa3......Fa(ka+1) 和 Ba(n-ka-1)......Ba1能够拼接成S并且发现字符Faka+1和Ba(n-ka).推论得证。


根据以上推论我们只需要求出对于任意字符x在前缀中出现的次数Fc 和 在后缀中出现的次数Bc。那么Fc * Bc 就是由于字符X产生的重复次数。

代码:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <queue>
#include <set>
#include <map>
#include <vector>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const int N = 10010;
const int M = 400010;
const int inf = 0x3f3f3f3f;
const ll oo = 0x3f3f3f3f3f3f3f3fll;
const int mod = 1e9 + 7;

#define pb push_back
#define sz(x) ((x).size())
#define mp make_pair

int n, tot;
int next[M][26];
char s[N][44];

void Reset(){
    tot = 0;
    for(int i = 0; i < 26; i++)
      next[tot][i] = -1;
}

int NewNode(){
    ++tot;
    for(int i = 0; i < 26; i++)
        next[tot][i] = -1;
    return tot;
}

int a[26], b[26];

void Insert(char s[], int cnt[]){
    int loc = 0;
    for(int i = 0; s[i]; i++){
        int c = s[i] - 'a';
        if(next[loc][c] == -1){
            next[loc][c] = NewNode();
            if(i) cnt[c]++;
        }
        loc = next[loc][c];
    }
    return ;
}

int main(){
    while(scanf("%d", &n) == 1){
        memset(a, 0, sizeof(a));
        memset(b, 0, sizeof(b));
        Reset();
        for(int i = 0; i < n; i++){
            scanf("%s", s[i]);
            Insert(s[i], a);
        }
        ll ret = tot;
        Reset();
        for(int i = 0; i < n; i++){
            reverse(s[i], s[i] + strlen(s[i]));
            Insert(s[i], b);
        }
        ret *= tot;
        for(int i = 0; i < 26; i++){
            ret -= 1ll * a[i] * b[i];
        }
        int c[26] = {};
        for(int i = 0; i < n; i++){
            if(strlen(s[i]) == 1 && !c[s[i][0] - 'a'])
                ret++, c[s[i][0] - 'a'] = 1;
        }
        cout << ret << endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值