【题解】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. Python Turtle绘画初学编程——六芒星,浪形圈

    老师上课说可以自学一下python中的绘图turtle,就自己初步学习了一下,做了两个简单的绘图——六芒星和浪形圈(其实我也不知道该叫它什么,就照样子编了个词

  2. kafka的主题与消费

    同一个消费者组不能同时消费同一个分区的数据 不同分区可以消费同一组不同消费者 同一个消费者可以同时消费多个topicA的数据 Topic和consumer依赖zookeeper,producer不依赖

  3. Docker基础概念与安装

    Docker是什么? Docker最初是dotCloud公司的创始人Solomon Hyks在法国期间发起的一个公司内部项目,它是基于dotCloud公司多年云服务技术的一次革新,并于2013年3月以 ...

  4. RV32FDQ/RV64RDQ指令集(1)

    Risc-V架构定义了可选的单精度浮点指令(F扩展指令集)和双精度浮点指令(D扩展指令集),以及四精度浮点指令集(Q扩展指令集).Risc-V架构规定:处理器可以选择只实现F扩展指令子集而不支持D扩展 ...

  5. 一文解读DevOps工具链 (转)

    在列出DevOps 工具链之前,介绍一下什么是DevOps,虽然DevOps这个概念现在还没有标准的定义,但我们可以追溯一下其过去九年的历史发展过程(从2009年-2017年),列出几个相对明确又有所 ...

  6. 熟悉的味道——从Java单例写到C++单例

    设计模式中,单例模式是常见的一种.单例模式需要满足以下两个条件: 保证一个类只能创建一个示例: 提供对该实例的全局访问点. 关于单例最经典的问题就是DCL(Double-Checked Lock),今 ...

  7. [20190510]rman备份的疑问7.txt

    [20190510]rman备份的疑问7.txt --//上午测试rman备份时备份文件大小回缩的测试.链接:--//http://blog.itpub.net/267265/viewspace-26 ...

  8. Linux使用Samba实现文件共享

    Samba服务是现在Linux系统与Windows系统之间共享文件的最佳选择. [root@study ~]# yum install samba -y #安装samba服务 [root@study ...

  9. C++学习03_对象

    1.类 类决定了一个对象具有什么样的属性功能. class MyFirstClass { }; //跟C++结构情况相似 //类的第一个字母采用大写是一种习惯的上的标准. //与C++结构情况类似 类 ...

  10. 【洛谷P2494】 [SDOI2011]保密(分数规划+最小割)

    洛谷 题意: 题意好绕好绕...不想写了. 思路: 首先类似于分数规划做法,二分答案得到到每个点的最小危险度. 然后就是在一个二分图中,两边撤掉最少的点(相应代价为上面算出的危险度)及相应边,使得中间 ...