【题解】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. 用ASP.NET Core构建可检测的高可用服务--学习笔记

    摘要 随着现代化微服务架构的发展,系统故障的定位与快速恢复面临着诸多挑战,构建可检测的服务,帮助线上保障团队时刻掌控应用的运行状况越来越重要.本次分享会讲解如何让 ASP .NET Core 应用与现 ...

  2. qt 界面去掉系统边框

    该代码在Qt5框架编辑,使用该类时, 直接继承这个类就可以了. 实现了拖拽功能和关闭功能,如果需要放大缩小功能, 需自己实现. 1 #ifndef CUSTOMIZE_QWIDGET_H #defin ...

  3. c# 字符串中全角和半角字符互转

    public class ConvertDBCAndSBC { /// <summary>半角转成全角 /// 半角空格32,全角空格12288 /// 其他字符半角33~126,其他字符 ...

  4. 深入理解JVM,虚拟机类加载机制

    类加载过程概览 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括以下7个阶段: 加载(Loading) 验证(Verification) 准备(Preparation) 解析(Re ...

  5. 如何利用python爬取网易新闻

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

  6. 服务端性能测试技能tree

    ALL: Left: Right: 摘抄一下(觉得不错) 以下来自百度百科 ---- 软件性能测试 软件性能测试是在交替进行负荷和强迫测试时常用的术语.理想的“软件性能测试”(和其他类型的测试)应在需 ...

  7. Debian创建.desktop文件(Create .desktop file in Debian/Gnome)

    在Debian系Linux中,用于标识应用的启动文件.desktop file是位于/usr/share/applications目录下的,Gnome会将这些文件在菜单中展示为启动图标,也可以固定在d ...

  8. CentOS7设置开机自启动方式

    方式一: # 在/etc/rc.d/rc.local文件中追加启动命令,该文件追加后,会随着机器自动后,自动运行文件中的命令 # vim /etc/rc.d/rc.local # 权限问题:在cent ...

  9. 1. git 基础

    参考文档: https://www.liaoxuefeng.com/wiki/896043488029600 https://git-scm.com/docs/git-fsck 一.介绍 Linus一 ...

  10. Markdown & LaTex 常用语法

    目录 blog 的目录 博客园自带目录 用 javascript 自定义目录 主标题 副标题 h1,一级标题 h2,二级标题 h3,三级标题 注释 常用的符号及文本形式 如果你想在markdown中文 ...