【题解】Puzzle [Uva1399]

【题解】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();
    }
}

原文地址:https://www.cnblogs.com/Xing-Ling/p/12040242.html

时间: 2024-10-17 19:09:06

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

题解【POJ1651】Multiplication Puzzle

Description The multiplication puzzle is played with a row of cards, each containing a single positive integer. During the move player takes one card out of the row and scores the number of points equal to the product of the number on the card taken

UVa 12107 Digit Puzzle 题解

难度:β 建议用时:45 min 实际用时:3 h 30 min ??(你看不出来这是题目链接,对吧?(手动滑稽)) 这是我目前为止独立完成的最复杂的一道题.(别喷我太水) 这样一道暴力搜索的题,怎么会花如此多时间? 因为我一直在改细节. 调试了很多.大多数时间都在调试.需要考虑的细节真的蛮多的. 下面分析算法. 这题我看有大神用数组 AC.然而本人因为水平有限,决定用字符串.于是时间肯定要爆.不过我有优化方法,过一会再说. 虽然用了优化,也只是 2500 ms 勉强过.?? 题目要求以最少的改

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

题意:给一个n*m的矩阵,你只能选择一个格子把这个格子的数换成p(也可以一个都不换),问最大子矩阵和最小可能是多少? 思路: 思路就是上面这个思路,这里简单讲一下怎么n^3求最大子矩阵和:枚举两行(或者两列),然后把每一列之和看做一个数字,这样二维就变成了一维,我们可以直接求最大子串和的方法.初始一个ret为0,然后从左往右加,如果ret<0,那么把ret初始化0,负数作为初始值肯定比重新开始小,然后找出ret的最大值就是最大子矩阵和. 代码: #include<cstdio> #inc

POJ 1651 Multiplication Puzzle 题解 区间DP

题目链接:http://poj.org/problem?id=1651[题目描述]<乘法谜题>乘法谜题源自这样一个背景,我们有一行 n 张牌,平铺在桌面上,每张牌的牌面上都标有一个正整数.玩家的初始得分是 0,他接下来要进行 n-2 次操作,每次操作他都需要从桌面上取出一张牌,然后他的得分会加上他取出的这张牌与取出的这张牌的左边的牌以及取出的这张牌的右边的牌的乘积.在第 n-2 次操作结束后,桌面上将会只剩下两张牌.你的目的是帮助玩家决定取牌的顺序,使得玩家的最终得分最小.举个例子,如果一开始

HDU 5465 Clarke and puzzle Nim游戏+二维树状数组

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5465 Clarke and puzzle Accepts: 42 Submissions: 269 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) 问题描述 克拉克是一名人格分裂患者.某一天,有两个克拉克(aa和bb)在玩一个方格游戏. 这个方格是一个n*mn∗m的矩阵,每个格子里有一

BestCoder Round #56 1002 Clarke and problem 1003 Clarke and puzzle

今天第二次做BC,不习惯hdu的oj,CE过2次... 1002 Clarke and problem #include<cstdio> #include<iostream> #include<string> #include<cstring> #include<queue> #include<vector> #include<stack> #include<vector> #include<map>

POJ 3221 Diamond Puzzle

Description A diamond puzzle is played on a tessellated hexagon like the one shown in Figure 1 below. And in this problem the faces produced by the tessellation are identified as they are numbered in the same figure. If two faces share a side, they a

POJ 2893 M &#215; N Puzzle(树状数组求逆序对)

                                                           M × N Puzzle Time Limit: 4000MS   Memory Limit: 131072K Total Submissions: 4112   Accepted: 1140 Description The Eight Puzzle, among other sliding-tile puzzles, is one of the famous problems

HDU 1097.A hard puzzle【快速幂或规律】【8月12】

A hard puzzle Problem Description lcy gives a hard puzzle to feng5166,lwg,JGShining and Ignatius: gave a and b,how to know the a^b.everybody objects to this BT problem,so lcy makes the problem easier than begin. this puzzle describes that: gave a and