【题解】Puzzle [Uva1399]

传送门:\(\text{Puzzle [Uva1399]}\)

【题目描述】

给定 \(m\) 和 \(n\),表示有 \(m\) 种不同的字符(大写字母\(A,B,C \cdots\)),\(n\) 个禁止串,请构造一个不包含任何禁止串最长字符串 并将其输出。如果可以无限长或者无解输出 \(No\),如果存在多解则输出字典序最大的一种。

【输入】

第一行一个整数T表示数据组数。

接下来每组数据第一行为两个整数 \(m,n\),接下来 \(n\) 行输入 \(n\) 个禁止串。

【输出】

一行表示答案。

【样例】

样例输入:
3
2 4
AAA
AB
BA
BB
2 4
AAA
BBB
ABAB
BBAA
3 7
AA
ABA
BAC
BB
BC
CA
CC 样例输出:
AA
No
ACBAB

【数据范围】

\(100\%:\) \(1 \leqslant m \leqslant 26,\) \(1 \leqslant n \leqslant 1000\)

【分析】

\(AC\) 自动机 \(+\) \(dp\) 的裸题。

按照套路,先对 \(n\) 个禁止串建立 \(AC\) 自动机,在每个禁止串的结尾节点处打个 \(end\) 标记,然后利用 \(fail\) 树向上传递标记。

用 \(dp[x]\) 表示从 \(AC\) 自动机上节点 \(x\) 开始往下 不经过禁止串结尾标记 所能延伸的最大长度,则有 \(dp[x]=max\{dp[tr[x][ch]]+1\}\) \((ch \in[0,m-1])\) 。

从根节点开始 \(dfs\),用 \(vis[x]\) 记录 \(x\) 节点是否在当前扫描出来的路径上,如果 \(vis[x]=1\),则说明在 \(AC\) 自动机上出现了合法的循环,如果一直沿着这个循环延伸下去,就可以构造出无限长的合法串。

为了防止超时,还需要记忆化,用 \(pan[x]\) 记录 \(x\) 节点是否已经搜过(注意 \(vis\) 和 \(pan\) 判断的先后顺序)。

至于输出答案,在 \(dp\) 转移时用一个辅助数组 \(g\) 记录最优决策点即可。

另外,这题有个简化版(只需要判断是否可以无限长):病毒 \(\text{[POI2000] [P2444]}\)

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#define LL long long
#define Re register int
using namespace std;
const int N=1003,M=5e4+3;
int n,C,T;char ch[53];
inline void in(Re &x){
int fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
struct AC_Automaton{
int O,g[M],ed[M],dp[M],vis[N],pan[M],fail[M],tr[M][26];queue<int>Q;
inline void CL(){
memset(fail,0,sizeof(fail));
memset(pan,0,sizeof(pan));
memset(vis,0,sizeof(vis));
memset(tr,0,sizeof(tr));
memset(ed,0,sizeof(ed));
memset(dp,0,sizeof(dp));
memset(g,-1,sizeof(g));
O=1;
}
inline void insert(char ch[]){
Re p=1;
for(Re i=1;ch[i];++i){
Re a=ch[i]-'A';//注意是大写A
if(!tr[p][a])tr[p][a]=++O;
p=tr[p][a];
}
ed[p]=1;
}
inline void get_fail(){
for(Re i=0;i<C;++i)tr[0][i]=1;
Q.push(1);
while(!Q.empty()){
Re x=Q.front();Q.pop();
for(Re i=0;i<C;++i)
if(tr[x][i])fail[tr[x][i]]=tr[fail[x]][i],Q.push(tr[x][i]);
else tr[x][i]=tr[fail[x]][i];
ed[x]|=ed[fail[x]];
}
}
inline int dfs(Re x){
if(vis[x])return 1;//在正在搜的路径中出现过x,即出现循环
if(pan[x])return 0;//已经搜过x了,肯定无果
vis[x]=pan[x]=1;
for(Re i=C-1,to;i>=0;--i)//注意求字典序最大
if(!ed[to=tr[x][i]]){
if(dfs(to))return 1;
if(dp[to]+1>dp[x])dp[x]=dp[to]+1,g[x]=i;
}
vis[x]=0;//搜过x后要换成fa[x]的另一条分支往下延伸,所以要还原成0
return 0;
}
inline void sakura(){
if(dfs(1))puts("No");//无限长
else{
Re ans=0;
for(Re i=1;i<=O;++i)if(dp[i]>dp[ans])ans=i;
if(!ans)puts("No");//无解
else{
Re p=1;
while(g[p]!=-1){
printf("%c",'A'+g[p]);
p=tr[p][g[p]];
}
puts("");
}
}
}
}AC;
int main(){
// freopen("123.txt","r",stdin);
in(T);
while(T--){
in(C),in(n),AC.CL();
while(n--)scanf("%s",ch+1),AC.insert(ch);
AC.get_fail(),AC.sakura();
}
}

【题解】Puzzle [Uva1399]的更多相关文章

  1. ZOJ 1602 Multiplication Puzzle(区间DP)题解

    题意:n个数字的串,每取出一个数字的代价为该数字和左右的乘积(1.n不能取),问最小代价 思路:dp[i][j]表示把i~j取到只剩 i.j 的最小代价. 代码: #include<set> ...

  2. 题解【POJ1651】Multiplication Puzzle

    Description The multiplication puzzle is played with a row of cards, each containing a single positi ...

  3. HihoCoder 1634 Puzzle Game(最大子矩阵和)题解

    题意:给一个n*m的矩阵,你只能选择一个格子把这个格子的数换成p(也可以一个都不换),问最大子矩阵和最小可能是多少? 思路: 思路就是上面这个思路,这里简单讲一下怎么n^3求最大子矩阵和:枚举两行(或 ...

  4. PAT甲题题解-1128. N Queens Puzzle (20)-做了一个假的n皇后问题

    博主欢迎转载,但请给出本文链接,我尊重你,你尊重我,谢谢~http://www.cnblogs.com/chenxiwenruo/p/6789810.html特别不喜欢那些随便转载别人的原创文章又不给 ...

  5. POJ1651:Multiplication Puzzle——题解

    http://poj.org/problem?id=1651 题目大意:同“乘法游戏”,这里将乘法游戏的题面复制过来. 乘法游戏是在一行牌上进行的.每一张牌包括了一个正整数.在每一个移动中,玩家拿出一 ...

  6. POJ3678:Katu Puzzle——题解

    http://poj.org/problem?id=3678 总觉得这题比例题简单. 设a为x取0的点,a+n为x取1的点. 我们还是定义a到b表示取a必须取b. 那么我们有: 当AND: 1.当c= ...

  7. 题解报告:hdu 1098 Ignatius's puzzle

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1098 题目中文是这样的: 伊格内修斯在数学上很差,他遇到了一个难题,所以他别无选择,只能上诉埃迪. 这 ...

  8. ZOJ 2836 Number Puzzle 题解

    题面 lcm(x,y)=xy/gcd(x,y) lcm(x1,x2,···,xn)=lcm(lcm(x1,x2,···,xn-1),xn) #include <bits/stdc++.h> ...

  9. 题解 CF613E Puzzle Lover

    解题思路 其实仔细观察我们可以发现路径一定是一个类似于下图的一个左括号之后中间随便反复曲折,然后右边在来一个右括号. 然后对于两个括号形状的东西其实是可以利用 Hash 来判等特殊处理的. 对于中间的 ...

随机推荐

  1. oracle学习笔记(九) SQL常用函数说明以及使用

    SQL常用函数说明以及使用 以下补充以下常用的函数,更多的请看oracle函数API文档 to_char to_char(8.58,'9.99') to_char(8.50,'9.00') to_ch ...

  2. MVC过滤器:自定义异常过滤器使用案例

    在上一篇文章中讲解了自定义异常过滤器,这篇文章会结合工作中的真实案例讲解一下如何使用自定义异常过滤器. 一.需求 本案例要实现的功能需求:在发生异常时记录日志,日志内容包括发生异常的Controlle ...

  3. winform批量更新数据_长时间的执行会导致界面卡死

    前言:使用winform触发一个事件后执行的代码,如果耗时非常长,则会导致窗口界面假死!  本人最近通过winform窗体执行一项:需要批量更新一批数据库的数据的操作的任务时,由于数据量达到百万级别, ...

  4. Python 爬取大众点评 50 页数据,最好吃的成都火锅竟是它!

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 胡萝卜酱 PS:如有需要Python学习资料的小伙伴可以加点击下方链 ...

  5. form分辨率

    近期做项目时,遇到开发的winform在自己电脑上可以正常显示,共享到其他电脑就事儿不能显示了: [转载自:http://blog.csdn.net/lcawen88/article/details/ ...

  6. 不能聚焦元素问题 WebDriverException: Message: unknown error: cannot focus element

    上周碰到了 Unable to locate element,即“无法定位元素”,后靠两行代码解决: wait = ui.WebDriverWait(driver,5) wait.until(lamb ...

  7. windows防火墙失效

    在某些情况下,我们希望阻止某款软件联网,比如防止软件更新. 通常来说使用windows自带的防火墙是可以阻止软件联网的,但在我的电脑上它却失效了,无法起到阻止软件联网的作用. 我的操作系统: OS 名 ...

  8. POJ3104Drying(二分)

    传送门 题目大意:n件衣服,每件有ai水分.每分钟可自然风干1个水分,或者放入机器中风干k个水分. 问最少花多长时间,衣服全干. 代码: #include<iostream> #inclu ...

  9. QBXT模拟赛2

    总结 期望得分:\(100 + 40 + 0 = 140\) 实际得分:\(0 + 0 + 0 = 0\) 鬼知道为什么我代码没有交上..自测\(10 + 50 + 0\)--这是心态爆炸的一场考试 ...

  10. Paper | Noise2Noise: Learning Image Restoration without Clean Data

    目录 故事背景 算法原理 点估计 神经网络算法与点估计的关系 核心思想 回头品味 实验 高斯 其他生成噪声 发表在2018 ICML. 摘要 We apply basic statistical re ...