洛谷P3065 [USACO12DEC]第一!First!(Trie树+拓扑排序)
P3065 [USACO12DEC]第一!First!
题目链接:https://www.luogu.org/problemnew/show/P3065
题目描述
Bessie一直在研究字符串。她发现,通过改变字母表的顺序,她可以按改变后的字母表来排列字符串(字典序大小排列)。
例如,Bessie发现,对于字符串串“omm”,“moo”,“mom”和“ommnom”,她可以使用标准字母表使“mom”排在第一个(即字典序最小),她也可以使用字母表“abcdefghijklonmpqrstuvwxyz”使得“omm”排在第一个。然而,Bessie想不出任何方法(改变字母表顺序)使得“moo”或“ommnom”排在第一个。
接下来让我们通过重新排列字母表的顺序来计算输入中有哪些字符串可以排在第一个(即字典序最小),从而帮助Bessie。
要计算字符串X和字符串Y按照重新排列过的字母表顺序来排列的顺序,先找到它们第一个不同的字母X[i]与Y[i],按重排后的字母表顺序比较,若X[i]比Y[i]先,则X的字典序比Y小,即X排在Y前;若没有不同的字母,则比较X与Y长度,若X比Y短,则X的字典序比Y小,即X排在Y前。
输入输出格式
输入格式:
* Line 1: A single line containing N (1 <= N <= 30,000), the number of strings Bessie is playing with.
* Lines 2..1+N: Each line contains a non-empty string. The total number of characters in all strings will be no more than 300,000. All characters in input will be lowercase characters 'a' through 'z'. Input will contain no duplicate strings.
第1行:一个数字N(1 <= N <= 30,000),Bessie正在研究的字符串的数量。
第2~N+1行:每行包含一个非空字符串。所有字符串包含的字符总数不会超过300,000。 输入中的所有字符都是小写字母,即a~z。 输入不包含重复的字符串。
输出格式:
* Line 1: A single line containing K, the number of strings that could be lexicographically first.
* Lines 2..1+K: The (1+i)th line should contain the ith string that could be lexicographically first. Strings should be output in the same order they were given in the input.
第1行:一个数字K,表示按重排后的字母表顺序排列的字符串有多少可以排在第一个数量。
第2~K+1行:第i+1行包含第i个按重排后的字母表顺序排列后可以排在第一个的字符串。字符串应该按照它们在输入中的顺序来输出。
输入输出样例
4
omm
moo
mom
ommnom
2
omm
mom
题解:
我们首先可以想到构建一颗Trie树,这样可以帮助我们节省一大波时间。通过分析问题,我们就可以发现,如果一个字符串的字典序要最小,首先其余任何一个字符串都不为它的前缀;并且,对于在同一层的其它点,字符的字典序可以满足最小。如果同时满足这两个条件的话,我们就可以认定它的字典序最小了。
前缀这个问题Trie树很好解决,主要就是如何判断字典序最小呢?这里我们可以用拓扑排序来求解,如果无环,那么就说明它是最小的。
具体做法就是枚举每一个串,假定它的字典序是最小的,然后就在Trie树上面一个一个找,一层一层连边直至末尾结点,最后通过拓扑排序判断一下就行了。
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,tot;
int val[],check[],vis[],head[],in[],Not[];
char s[];
string str[];
struct Edge{
int u,v,next;
}e[];
void adde(int u,int v){
e[tot].u=u;e[tot].v=v;e[tot].next=head[u];head[u]=tot++;
}
struct Trie{
char node;
int son,left;
Trie(){son=left=-;}
}tre[];
void init(){
tre[].son=tre[].left=-;tot=;
}
void insert(int now){
int j,u=;
int l = strlen(s);
for(int i=;i<l;i++){
bool flag=false;
for(j=tre[u].son;j!=-;j=tre[j].left){
if(tre[j].node==s[i]){
flag=true;
break ;
}
}
if(!flag){
j=tot++;
tre[j].left=tre[u].son;
tre[u].son=j;
tre[j].node=s[i];
tre[j].son=-;
}
u=j;
}
val[u]=;
}
int Use(string S){
memset(head,-,sizeof(head));tot=;
memset(in,,sizeof(in));memset(vis,,sizeof(vis));
int l=S.length();
int u=,j,Next;
for(int i=;i<l;i++){
for(j=tre[u].son;j!=-;j=tre[j].left){
int u=S[i]-'a',v=tre[j].node-'a';
if(u==v){
Next=j;
continue ;
}
adde(u,v);vis[u]=vis[v]=;in[v]++;
}
u=Next;
if(val[u]&&i!=l-) return ;
}
return ;
}
int top_sort(){
queue <int> q;
int cnt = ,num = ;
for(int i=;i<;i++) if(!in[i]&&vis[i]) q.push(i),cnt++;
for(int i=;i<;i++) if(vis[i]) num++;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i!=-;i=e[i].next){
int v=e[i].v;
if(--in[v]==) q.push(v),cnt++;
}
}
return cnt==num;
}
int main(){
cin>>n;
init();
for(int i=;i<=n;i++){
scanf("%s",s);
str[i]=s;
insert(i);
}
int ans = ;
for(int i=;i<=n;i++){
if(Use(str[i])&&top_sort()) check[i]=,ans++;
}
printf("%d\n",ans);
for(int i=;i<=n;i++){
if(check[i]) cout<<str[i]<<'\n';
}
return ;
}
洛谷P3065 [USACO12DEC]第一!First!(Trie树+拓扑排序)的更多相关文章
- [USACO12DEC]第一!First! (Trie树,拓扑排序)
题目链接 Solution 感觉比较巧的题啊... 考虑几点: 可以交换无数次字母表,即字母表可以为任意形态. 对于以其他字符串为前缀的字符串,我们可以直接舍去. 因为此时它所包含的前缀的字典序绝对比 ...
- 「Usaco2012 Dec」第一(字典树+拓扑排序)
(我恨字符串) 惯例化简题目:给定n个字符串,可以改变字符的相对大小(在字典序中的大小),问:字符串i是否能成为最小的字符串(字典序) 解题过程: 首先你可以预处理出来26的全排列然后暴力然后你只要用 ...
- 洛谷P3244 落忆枫音 [HNOI2015] 拓扑排序+dp
正解:拓扑排序+dp 解题报告: 传送门 我好暴躁昂,,,怎么感觉HNOI每年总有那么几道题题面巨长啊,,,语文不好真是太心痛辣QAQ 所以还是要简述一下题意,,,就是说,本来是有一个DAG,然后后来 ...
- 洛谷P2017 [USACO09DEC]晕牛Dizzy Cows [拓扑排序]
题目传送门 晕牛Dizzy Cows 题目背景 Hzwer 神犇最近又征服了一个国家,然后接下来却也遇见了一个难题. 题目描述 The cows have taken to racing each o ...
- P3065 [USACO12DEC]第一!First!
题目描述 Bessie has been playing with strings again. She found that by changing the order of the alphabe ...
- Luogu P3065 [USACO12DEC]第一!First!【字典树/拓扑排序】By cellur925
题意:给你许多字符串,你可以改变字母序大小,问有哪些字符串可能成为字典序最小的字符串. 我们考虑把这些字符串都塞到\(trie\)树上.之后检索每一个字符串的时候,我们看和他同一层的地方是否有字符,如 ...
- [luogu P3065] [USACO12DEC]第一!First!
[luogu P3065] [USACO12DEC]第一!First! 题目描述 Bessie has been playing with strings again. She found that ...
- 洛谷 P3373 【模板】线段树 2
洛谷 P3373 [模板]线段树 2 洛谷传送门 题目描述 如题,已知一个数列,你需要进行下面三种操作: 将某区间每一个数乘上 xx 将某区间每一个数加上 xx 求出某区间每一个数的和 输入格式 第一 ...
- 牛客练习赛11 假的字符串 (Trie树+拓扑找环)
牛客练习赛11 假的字符串 (Trie树+拓扑找环) 链接:https://ac.nowcoder.com/acm/problem/15049 来源:牛客网 给定n个字符串,互不相等,你可以任意指定字 ...
随机推荐
- lintcode671 循环单词
循环单词 The words are same rotate words if rotate the word to the right by loop, and get another. Cou ...
- 平衡的括号 (Parentheses Balance UVA - 673)
题目描述: 原题:https://vjudge.net/problem/UVA-673 题目思路: 1.水题 2.栈+模拟 3.坑在有空串 AC代码 #include <iostream> ...
- CryptoZombies学习笔记——Lesson4
第四课主要介绍payable函数相关. chapter1: payable修饰函数 以太坊允许同时调用函数和eth转账.msg.value显示发送到合约的以太币数,ether是内置整型数.如果函数没有 ...
- Python3 循环表达式
一 While循环 基本循环 while 条件: 执行内容 #循环体 ... #循环体 ... #循环体 # 若条件为真,执行循环体内容 # 若条件为假,不执行循环体内容 实例1(Python 3.0 ...
- 原生js 自定义confirm
本文参考博客园另一篇文章:https://www.cnblogs.com/hzj680539/p/5374052.html,在此感谢. 在实际开发当中,考虑到原生js组件,包括alert.confir ...
- phpcms找不到模板、空白模板的解决办法
有时候会出现这个问题,尤其是在调试模板的过程中,不知道别的朋友的操作习惯.我的习惯是,先保留一份原始的模板.比如: index原版.html. 以便对比之用.但是呢,这样,往往会造成 找不到模板的情况 ...
- 统计VS2013中有效行数
将鼠标放在解决方案处,按下ctrl+shift+F b*[^:b#/]+.*$(带前面的using)^b*[^:b#/]+.*$
- 微信支付java
直接上代码: 1.支付配置PayCommonUtil import com.legendshop.payment.tenpay.util.MD5Util; import com.legendshop. ...
- c# 调用c++ 使用指针传递的时候
http://www.cnblogs.com/warensoft/archive/2011/12/09/warenosoft3d.html 上面这篇文章很好解释了. 简单记录一下: 1. 声明 注意 ...
- RT-thread finsh组件工作流程
finsh是RT-Thread的命令行外壳(shell),提供一套供用户在命令行的操作接口,主要用于调试.查看系统信息.在大部分嵌入式系统中,一般开发调试都使用硬件调试器和printf日志打印,在有些 ...