题目大意:给出n个旧单词,要从这n个旧单词中构造新单词。构造条件是 S = Sa + Sb,其中Sa为某个旧单词的非空前缀,Sb为某个单词的非空后缀。求所有的新单词和旧单词中有多少个不同的单词。

思路:将所有单词建成一棵字典树,再将所有单词反转并建成一棵字典树。则第一棵树的结点个数即为不同前缀的数量,第二棵树的结点个数为不同后缀的数量。如果不算重复,取两者的乘积即为新单词的数量。

接下来考虑重复的情况。什么样子会重复呢?假设第一棵树中某个前缀为a1a2a3...X,第二棵树中某个后缀为Xb1b2b3...。那么这就一定会有重复,也就是前缀的末字符和后缀的首字符相同时会重复。考虑a1a2a3...Xb1b2b3...,这个X可能出现在前缀里,也可能出现在后缀里,这也就是多算了一种情况。考虑一般情况,假设第一棵树中某个前缀为a1a2a3...XXX..X(一共m个X),第二棵树中某个后缀为XXX...Xb1b2b3...(一共n个X),那么这两个串组成的串a1a2a3XX...Xb1b2b3...中X的数目有m+n+1种可能(0~m+n),而前缀中X的数目有(m+1)种可能(0~m个),同理后缀中X的数目(n+1)种可能,因此我们在实际计算中一共多算了(m+1)*(n+1) - (m+n+1) = m*n次,即前缀中X的数目与后缀中X数目的乘积。

那么,我们只要分别求出两棵树中每个字符的出现次数并减去它们的乘积即可(这是用了加法乘法原理,假设第一棵树中末尾为X的前缀共有3个,分别含a1、a2、a3个X;第二棵树中开头为X的后缀为3个,分别含b1、b2、b3个X,则一共重复算了a1*b1+a1*b2+a1*b3+a2*b1+a2*b2+a2*b3+a3*b1+a3*b2+a3*b3=(a1+a2+a3)*(b1+b2+b3)次)。这里要注意,只统计深度大于1的结点,因为我们要保证前后缀均非空(如前缀X与后缀Xb就不会有重复,因为第一个的X必须要选)。最后,再加上长度为1的旧单词即可,因为我们构造单词时不会造出长度为1的单词。

#include<cstdio>
#include<cstring>
#include<string>
#include<cctype>
#include<iostream>
#include<set>
#include<map>
#include<cmath>
#include<sstream>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#define fin freopen("a.txt","r",stdin)
#define fout freopen("a.txt","w",stdout)
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int inf = 1e9 + ;
const int maxnode = 4e5 + ;
const int sigma_size = ;
const int maxn = + ;
char s[];
int vis[sigma_size]; struct Tree
{
int ch[maxnode][sigma_size];
int val[maxnode];
int cnt[sigma_size];
int sz;
int idx(char c) { return c - 'a'; }
void init() { memset(ch[], , sizeof ch[]); sz = ; memset(cnt, , sizeof cnt); } void insert(char *s)
{
int n = strlen(s), u = ;
for(int i = ; i < n; i++)
{
int c = idx(s[i]);
if(!ch[u][c])
{
memset(ch[sz], , sizeof ch[sz]);
val[sz] = ;
ch[u][c] = sz++;
if(i) cnt[c]++;
}
u = ch[u][c]; }
val[u] = ;
} }Pre, Suf; int main()
{ int n;
while(scanf("%d", &n) == )
{
Pre.init(); Suf.init();
memset(vis, , sizeof vis);
for(int i = ; i <= n; i++)
{
scanf("%s", s);
Pre.insert(s);
int len = strlen(s);
reverse(s, s+len);
Suf.insert(s);
if(len == ) vis[s[]-'a'] = ;
}
LL ans = LL(Pre.sz-)*LL(Suf.sz-);
for(int i = ; i < sigma_size; i++)
ans -= (LL)Pre.cnt[i] * LL(Suf.cnt[i]);
for(int i = ; i < sigma_size; i++)
if(vis[i]) ++ans;
cout << ans << endl;
}
return ;
}

UVA5913 Dictionary Sizes(字典树)(转载)的更多相关文章

  1. uva 1519 - Dictionary Size(字典树)

    题目链接:uva 1519 - Dictionary Size 题目大意:给出n个字符串组成的字典.如今要加入新的单词,从已有单词中选出非空前缀和非空后缀,组成新单词. 问说能组成多少个单词. 解题思 ...

  2. [转载]字典树(trie树)、后缀树

    (1)字典树(Trie树) Trie是个简单但实用的数据结构,通常用于实现字典查询.我们做即时响应用户输入的AJAX搜索框时,就是Trie开始.本质上,Trie是一颗存储多个字符串的树.相邻节点间的边 ...

  3. 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)

    前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...

  4. [LeetCode] Implement Trie (Prefix Tree) 实现字典树(前缀树)

    Implement a trie with insert, search, and startsWith methods. Note:You may assume that all inputs ar ...

  5. 萌新笔记——C++里创建 Trie字典树(中文词典)(一)(插入、遍历)

    萌新做词典第一篇,做得不好,还请指正,谢谢大佬! 写了一个词典,用到了Trie字典树. 写这个词典的目的,一个是为了压缩一些数据,另一个是为了尝试搜索提示,就像在谷歌搜索的时候,打出某个关键字,会提示 ...

  6. 字典树 - A Poet Computer

    The ACM team is working on an AI project called (Eih Eye Three) that allows computers to write poems ...

  7. poj 2503:Babelfish(字典树,经典题,字典翻译)

    Babelfish Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 30816   Accepted: 13283 Descr ...

  8. hdu 1247:Hat’s Words(字典树,经典题)

    Hat’s Words Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...

  9. hdu 1075:What Are You Talking About(字典树,经典题,字典翻译)

    What Are You Talking About Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 102400/204800 K ...

随机推荐

  1. PS/2的相关知识

    PS/2接口 很多微机上采用PS/2口来连接鼠标和键盘.PS/2接口与传统的键盘接口除了在接口外型.引脚有不同外,在数据传送格式上是相同的.现在很多主板用PS/2接口插座连接键盘,传统接口的键盘可以通 ...

  2. Java工程师 基础+实战 完整路线图(详解版)

    Java工程师 基础+实战 完整路线图(详解版)   Java 基础 Java 是一门纯粹的面向对象的编程语言,所以除了基础语法之外,必须得弄懂它的 oop 特性:封装.继承.多态.此外还有泛型.反射 ...

  3. git利用hooks实现自动部署

    准备工作: 1.一台虚拟linux环境和window 开始工作 1.安装git(略) 2.创建git用户和创建test.git裸仓库 [root@localhost ~]# useradd -m gi ...

  4. P4550 收集邮票

    P4550 收集邮票 题目描述 有n种不同的邮票,皮皮想收集所有种类的邮票.唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是n种邮票中的哪一种是等概率的,概率均为1/n.但是由 ...

  5. TornadoFx学习笔记(1)——常用的代码片段

    Tornadofx是基于JavaFx的一个kotlin实现的框架 之后看情况补充.. 1.读取resources文件夹中的文件 如图 想要读取config.properties文件,有两种方法 在cl ...

  6. item方法

    class Person: def __init__(self, name, age): self.name = name self.age = age def __getitem__(self, i ...

  7. 十大排序算法(Java实现)

    一.冒泡排序(Bubble Sort) public class BubbleSort { public static void main(String[] args) { int[] arr = { ...

  8. 常用API_1

    API API(Application Programming Interface),应用程序编程接口.Java API是一本程序员的 字典 ,是JDK中提供给 我们使用的类的说明文档.这些类将底层的 ...

  9. 安全性与收尾工作 运用过滤器进行授权 精通ASP-NET-MVC-5-弗瑞曼

  10. 绕过路由系统 (Bypassing the Routing System)| 高级路由特性