【题解】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. Spark Streaming Listener 监控批次处理延迟进行告警

    概述 StreamingListener 是针对spark streaming的各个阶段的事件监听机制. StreamingListener接口 //需要监听spark streaming中各个阶段的 ...

  2. C# - VS2019页面布局容器splitContainer和groupBox小结

    前言 在WinFrm应用程序中,产品的外观.布局将直接影响用户第一体验,所以对于开发者来说,在没有美工支持的前提下,应当注意系统页面的布局,本章主要讲解splitContainer和groupBox的 ...

  3. FCC---Create Visual Direction by Fading an Element from Left to Right---一个带好看背景色的圆形图案,从左到右移动,透明度opacity渐变为0.1,背景色渐渐消失的效果

    For this challenge, you'll change the opacity of an animated element so it gradually fades as it rea ...

  4. 【转载】Android 的 Handler 机制实现原理分析

    handler在安卓开发中是必须掌握的技术,但是很多人都是停留在使用阶段.使用起来很简单,就两个步骤,在主线程重写handler的handleMessage( )方法,在工作线程发送消息.但是,有没有 ...

  5. [转]关于maven pom.xml中dependency type 为pom的应用

    原文地址:http://blog.csdn.net/yao123long/article/details/49925659 dependency为什么会有type为pom,默认的值是什么?depend ...

  6. 记录C#-WPF布局面板

    StackPanel:适合水平或者垂直方向的布局 DockPanel:区域布局 WrapPanel:自动换行的StackPanel布局 Grid:网格布局

  7. Troubleshooting ORA-01555/ORA-01628/ORA-30036 During Export and Import (Doc ID 1579437.1)

    Troubleshooting ORA-01555/ORA-01628/ORA-30036 During Export and Import (Doc ID 1579437.1) APPLIES TO ...

  8. 7. Vue - 组件

    一.组件分类 1. 定义 ​ 组件是可以扩展HTML元素,封装可重用的代码.在较高层面上,组件是自定义元素.特点为:代码重用,提高开发效率,让网页结构更清晰. 2. 局部组件 ​ 只能在定义它的el中 ...

  9. Vue props中Object和Array设置默认值

    Vue中,在props中设置Object和Array的默认值 seller: { type: Object, default() { return {} } } seller: { type: Obj ...

  10. Redis缓存策略

    常用策略有“求留余数法”和“一致性HASH算法” redis存储的是key,value键值对 一.求留余数法 使用HASH表数据长度对HASHCODE求余数,余数作为索引,使用该余数,直接设置或访问缓 ...