bzoj1559 [JSOI2009]密码

题目链接:[JSOI2009]密码

我们先看第一问:输出方案数

我们把所有给出来的串丢到AC自动机里面去,然后在建出来的\(trie\)图上跑dp

由于\(n\leq 10\)我们很自然的就想到了状压

记\(dp[i][j][sta]\)表示原串匹配到了第\(i\)位,在AC自动机里走到了第\(j\)个节点,已经出现了\(sta\)个单词(压缩状态)的方案数

注意到如果有两个串\(s_i\)和\(s_j\)满足\(s_i\)是\(s_j\)的子串,那么我们所构建的串并不一定必须要有\(s_i\),因为如果有了\(s_j\)那么一定满足了必须有\(s_i\)

那么我们可以确定最终统计答案时的\(sta\),暴力累加即可

接下来就是第二问:输出方案

我么先要考虑一下在什么情况需要做第二问,题目中给的条件是\(ans\leq 42\)

如果此时我们构建的字符串一定有一位“空闲”,即一定有一位可以放置任意字符,而剩下的则必须构成给定的字符串,那么方案数就是\(2*26=52>42\)(该字符可以放在开头和末尾)

因此此时一定不需要输出答案

所以当要进行第二问的时候,一定是只能用所给定的串进行组合

并且不可能出现的重复的串,因为重复的串是可以变成空闲节点的,这样就和上面一样了

预处理出对于任意两个字符串\(s_i,s_j\),\(s_i\)的后\(k\)位和\(s_j\)的前\(k\)位相等的所有\(k\)值

然后暴力dfs排序顺序即可

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int maxd=1000000007,N=100000;
const double pi=acos(-1.0);
typedef long long ll;

namespace AC{
    struct node{
        int ch[27];
        int fail,end;
        node() {memset(ch,0,sizeof(ch));fail=0;end=0;}
    }point[100100];
    int cnt;

    void init()
    {
        cnt=0;
        point[0].fail=0;
    }

    void insert(char *s,int id)
    {
        int len=strlen(s),i,now=0;
        for (i=0;i<len;i++)
        {
            if (!point[now].ch[s[i]-'a'])
            {
                point[now].ch[s[i]-'a']=(++cnt);
                point[cnt]=node();
            }
            now=point[now].ch[s[i]-'a'];
        }
        point[now].end=(1<<(id-1));
    }

    void get_fail()
    {
        queue<int> q;
        int i;
        for (i=0;i<26;i++)
        {
            if (point[0].ch[i])
            {
                point[point[0].ch[i]].fail=0;
                q.push(point[0].ch[i]);
            }
        }
        while (!q.empty())
        {
            int u=q.front();q.pop();
            for (i=0;i<26;i++)
            {
                if (point[u].ch[i])
                {
                    point[point[u].ch[i]].fail=point[point[u].fail].ch[i];
                    q.push(point[u].ch[i]);
                }
                else point[u].ch[i]=point[point[u].fail].ch[i];
            }
        }
    }
}
using namespace AC;
int n,m,len[20],restm,LINK[50][50],ansord[50];
ll dp[2][110][1030];
char s[20][20];
bool del[20];

int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

int getl(int u,int v)
{
    int maxl,i;
    for (maxl=min(len[u],len[v]);maxl>0;maxl--)
    {
        bool ok=1;
        for (i=0;i<maxl;i++)
        {
            if (s[v][i]!=s[u][len[u]-maxl+i]) {ok=0;break;}
        }
        if (ok) return maxl;
    }
    return 0;
}

int ord[50],tot=0;
bool vis[50];
char ansch[110][110];

void dfs(int dep)
{
    if (dep>restm)
    {
        tot++;
        int i,j,now=0;
        for (i=1;i<=restm;i++)
        {
            for (j=LINK[ord[i-1]][ord[i]];j<len[ord[i]];j++)
            {
                ansch[tot][now++]=s[ord[i]][j];
                if (now>n) {tot--;return;}
            }
        }
        if (now<n) tot--;
        return;
    }

    int i;
    for (i=1;i<=m;i++)
    {
        if ((del[i]) || (vis[i])) continue;
        ord[dep]=i;vis[i]=1;
        dfs(dep+1);
        vis[i]=0;
    }
}

bool cmp(int p,int q)
{
    int i;
    for (i=0;i<n;i++)
        if (ansch[p][i]!=ansch[q][i]) return ansch[p][i]<ansch[q][i];
}

int main()
{
    n=read();m=read();restm=m;
    int i,j,k;
    for (i=1;i<=m;i++) {scanf("%s",s[i]);len[i]=strlen(s[i]);}
    for (i=1;i<=m;i++)
    {
        for (j=1;j<=m;j++)
        {
            if ((del[i]) || (del[j]) || (i==j)) continue;
            if (len[i]<len[j]) continue;
            if (strstr(s[i],s[j])) {del[j]=1;restm--;break;}
        }
    }
    init();
    for (i=1;i<=m;i++)
        if (!del[i]) insert(s[i],i);
    get_fail();
    dp[0][0][0]=1;
    int now=1,pre=0;
    for (i=1;i<=n;i++)
    {
        memset(dp[now],0,sizeof(dp[now]));
        for (j=0;j<=cnt;j++)
        {
            for (k=0;k<(1<<m);k++)
            {
                if (dp[pre][j][k])
                {
                    int p;
                    for (p=0;p<26;p++)
                    {
                        int v=point[j].ch[p];
                        dp[now][v][(k|point[v].end)]+=dp[pre][j][k];
                    }
                }
            }
        }
        now^=1;pre^=1;
    }
    int all=0;
    for (i=1;i<=m;i++)
        if (!del[i]) all|=(1<<(i-1));
    ll ans=0;
    for (i=0;i<=cnt;i++) ans+=dp[pre][i][all];
    printf("%lld\n",ans);
    if (ans<=42)
    {
        for (i=1;i<=m;i++)
        {
            for (j=1;j<=m;j++)
            {
                if ((del[i]) || (del[j])) continue;
                LINK[i][j]=getl(i,j);
            }
        }
        memset(vis,0,sizeof(vis));
        dfs(1);
        for (i=1;i<=ans;i++) ansord[i]=i;
        sort(ansord+1,ansord+1+tot,cmp);
        for (i=1;i<=tot;i++)
        {
            for (j=0;j<n;j++) putchar(ansch[ansord[i]][j]);
            puts("");
        }
    }
    return 0;
}

bzoj

原文地址:https://www.cnblogs.com/zhou2003/p/10468634.html

时间: 2024-11-02 14:27:31

bzoj1559 [JSOI2009]密码的相关文章

[BZOJ1559][JSOI2009]密码(AC自动机)

http://www.lydsy.com/JudgeOnline/problem.php?id=1559 2009年的省选题虽然比起现在简单了不少,但对我来说还是很有挑战性的. 首先对于这种多串匹配问题,第一个想到的就应该是AC自动机. 还是老套路,f[i][j]表示走到字符串的第i位,现在在自动机上的第j位时的信息.增加一维n位的压位表示各个串是否都被匹配到了. 但是这里有个问题,如果S1包含了S2,那么我们只需要S1被匹配就能保证S2也被匹配,而不需要在自动机上走到S2的位置. 这样我们预处

BZOJ 1559: [JSOI2009]密码( AC自动机 + 状压dp )

建AC自动机后, dp(x, y, s)表示当前长度为x, 在结点y, 包括的串的状态为s的方案数, 转移就在自动机上走就行了. 对于输出方案, 必定是由给出的串组成(因为<=42), 所以直接暴搜答案. 数据范围很小, 可以AC(复杂度懒得算了....) ------------------------------------------------------------------------------------------------ #include<cstdio> #in

[JSOI2009]密码

Description Input Output Sample Input 10 2 hello world Sample Output 2 helloworld worldhello HINT 一看\(n\)这么小就要状压--我们设\(f[i][j][s]\)表示长度为\(i\),AC自动机上节点为\(j\),出现的字符串的状态为\(s\)的方案数,然后直接枚举转移即可 然后难点就在于如何输出方案 首先42这数字非常妙(生命.宇宙以及任何事情的终极答案) 如果存在一个字符可以任意选的情况,那么

[BZOJ1559]密码 AC自动机+状压

问题 K: [JSOI2009]密码 时间限制: 1 Sec  内存限制: 64 MB 题目描述 众所周知,密码在信息领域起到了不可估量的作用.对于普通的登陆口令,唯一的破解 方法就是暴力破解一逐个尝试所有可能的字母组合,但这是一项很耗时又容易被发现的工 作.所以,为了获取对方的登陆口令,在暴力破解密码之前,必须先做大量的准备工作.经 过情报的搜集,现在得到了若干有用信息,形如: “我观察到,密码中含有字符串***.” 例如,对于一个10位的密码以及观察到的字符串hello与world,可能的密

密码算法详解——AES

0 AES简介 美国国家标准技术研究所在2001年发布了高级加密标准(AES).AES是一个对称分组密码算法,旨在取代DES成为广泛使用的标准. 根据使用的密码长度,AES最常见的有3种方案,用以适应不同的场景要求,分别是AES-128.AES-192和AES-256.本文主要对AES-128进行介绍,另外两种的思路基本一样,只是轮数会适当增加. 1 算法流程 AES加解密的流程图如下: AES加密过程涉及到4种操作:字节替代(SubBytes).行移位(ShiftRows).列混淆(MixCo

linux 本地账号密码无法登陆,一直返回 登陆的login界面

登陆redhat一直是返回login,账号和密码没错 通过ssh crt类的软件远程连接系统 然后更改文件   vi /etc/pam.d/login 把 :session required /lib/security/pam_limits.so 更改为:session required /lib64/security/pam_limits.so wq保存

凯撒密码、GDP格式化输出、99乘法表

1凯撒密码加密plaincode=input('请输入明文:')print('密文:',end='')for i in plaincode:print(chr(ord(i)+3),end='') 2.国家名称 GDP总量(人民币亿元) 中国 ¥765,873.4375澳大利亚 ¥ 78,312.4375 print('国家名称 GDP总量(人民币亿元)')print('{0:''<12}¥{1:''>10,.2f}'.format('中国',765873.4375))print('{0:''&

洛谷 P1079 Vigen&#232;re 密码 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1079 题目描述 16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法――Vigenère 密 码.Vigenère 密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为 南军所广泛使用. 在密码学中,我们称需要加密的信息为明文,用 M 表示:称加密后的信息为密文,用 C 表示:而密钥是一种

Mysql的更改密码

检查mysql的服务是否能开启,保证mysql的服务能正常开启,进行一下步骤 第一步:将你的Mysql解压到C盘根目录 第二步:(解压后进入目录,找到install.bat文件,用Ediplus打开,找到 --install mysql 和 net start mysql,两个mysql必须一样)进入cmd命令,没有配置环境变量的需要手动切换到mysql\bin的目录下cd命令进入 第三步:使用命令登陆mysql(mysql -localhost -u root),输入原始密码. 第四步: my