Censored!

  题目大意:给定一些字符,将这些字符组成一个固定长度的字符串,但是字符串不能包含一些禁词,问你有多少种组合方式。

  这是一道好题,既然出现了“一些”禁词,那么这题肯定和AC自动机有点关系了,对于这一题来说,因为我们需要的是求出在N^M种状态除去包含禁词的状态数,枚举肯定是不现实的了,我们唯一能做的只能是DP,但是怎么DP呢?只能是通过AC自动机来想了,由此我们来看一下trie树,我们知道trie树是可以表示不同字符串前后缀的转移关系的,但是这一题并不是要我们求出字串中是否有禁词,而是要我们求出除了禁词的其他组合方式,那么我们可不可以通过trie树直接看出来呢?答案是可以的,但是我们要改进一下。

  如果我们把trie树中的Next数组补齐,比如在ACGT中含有禁词ACC,C,我们可以构建如下图的一个trie树

                

  

  我们可以看到这棵trie树补齐了一般trie树不存在的边,其实就是把不存在的对应k子节点指向当前节点的fail节点的k节点,这样我们就可以找到所有元素转移的状态关系了,得到了这个东西,那么我们就可以用DP来把状态转移全部搞出来了,组成一个m长度的单词,只要我们不从失败状态(禁词的结尾)转出或者转入就OK了。

  这样一开DP就很容易理解了,状态转移方程dp[i+1][转移状态]=dp[i][trie树上某个节点]+dp[i+1][转移状态](转移状态是指的是其他节点转移过来的情况),那么怎么标定非法情况呢?我们不仅要把单词结尾标记为非法状态,如果当前位置fail指针的指向的k位置也为单词结尾,那么当前位置的指向的k位置也必须标记为非法状态,因为我们不能从这个节点转出(也就意味着这个单词是包含着另一个单词的)。

  

 #include <iostream>
#include <algorithm>
#include <functional>
#define MAX 130 using namespace std; static int sum_node, Hash_Table[MAX];
static char str[]; struct node
{
int if_end,num;//注意end位置不仅是标记单词的结束,而且还表示了是否能状态转移
node *fail, *Next[MAX];
}Mem_Pool[MAX], *Trie_Node[MAX], *Queue[MAX * ];
struct BigInterget
{
int A[];
enum { MOD = };
BigInterget()//构析函数用于初始化大数,A[0]表示大数的长度
{
memset(A, , sizeof(A));
A[] = ;
}
void set(int x)//用于设定一个32位的整数
{
memset(A, , sizeof(A));
A[] = ; A[] = x;
}
void print(void)
{
printf("%d", A[A[]]);
for (int i = A[] - ; i > ; i--)
{
if (A[i] == ){ printf(""); continue; }
for (int k = ; k*A[i] < MOD; k *= )
printf("");
printf("%d", A[i]);
}
printf("\n");
}
int& operator [] (int pos) { return A[pos]; }
const int& operator [] (int pos) const { return A[pos]; } BigInterget operator + (const BigInterget &B)
{
BigInterget C;
C[] = max(A[], B[]);
for (int i = ; i <= C[]; i++)
C[i] += A[i] + B[i], C[i + ] += C[i] / MOD, C[i] %= MOD;
if (C[C[] + ] > )C[]++;//进位
return C;
}
};
static BigInterget dp[][MAX]; struct node *create_new_node(void);
void put_letter_to_hash(const int);
void Insert(struct node *);
void build_ac_automation(struct node *); int main(void)
{
int Word_Length, Forbidden_Word_Sum, Letter_Type_Sum;
while (~scanf("%d%d%d", &Letter_Type_Sum, &Word_Length, &Forbidden_Word_Sum))
{
sum_node = ;
memset(Hash_Table, , sizeof(Hash_Table));
node *root = create_new_node();
put_letter_to_hash(Letter_Type_Sum); for (int i = ; i < Forbidden_Word_Sum; i++)
Insert(root);
build_ac_automation(root); for (int i = ; i <= Word_Length; i++)
for (int j = ; j < sum_node; j++)
dp[i][j] = BigInterget();
dp[][].set(); for (int i = ; i < Word_Length; i++)
for (int j = ; j < sum_node; j++)
{
if (Mem_Pool[j].if_end)//不能从非法状态中转入
continue;
for (int k = ; k < Letter_Type_Sum; k++)
{
if (Mem_Pool[j].Next[k]->if_end)//不能从非法状态中转出
continue;
int id = Mem_Pool[j].Next[k]->num;
dp[i + ][id] = dp[i][j] + dp[i + ][id];
}
}
BigInterget ans = BigInterget();
for (int i = ; i < sum_node; i++)
ans = ans + dp[Word_Length][i];
ans.print();
}
return EXIT_SUCCESS;
} struct node *create_new_node(void)
{
node *tmp = &Mem_Pool[sum_node];
tmp->fail = NULL;
tmp->if_end = ;
tmp->num = sum_node++;
memset(tmp->Next, NULL, sizeof(struct node*)*MAX);
return tmp;
} void put_letter_to_hash(const int Letter_Type_Sum)
{
getchar();//去掉回车换行
gets(str); for (int i = ; i < Letter_Type_Sum; i++)
Hash_Table[str[i]] = i;
} void Insert(struct node *root)
{
gets(str);
struct node *tmp_ptr = root; for (int i = ; str[i] != '\0'; i++)
{
int id = Hash_Table[str[i]];
if (tmp_ptr->Next[id] == NULL)
tmp_ptr->Next[id] = create_new_node();
tmp_ptr = tmp_ptr->Next[id];
}
tmp_ptr->if_end = ;
} void build_ac_automation(struct node *root)
{
int head = , tail = ;
node *out = NULL;
root->fail = NULL;
Queue[tail++] = root; while (head != tail)
{
out = Queue[head++];
for (int i = ; i < MAX; i++)
if (out->Next[i] != NULL)
{
if (out == root)
out->Next[i]->fail = root;
else
{
out->Next[i]->fail = out->fail->Next[i];
//如果还找到在其他地方找到和他一样的元素,那么我们就把失败指针指向这个元素,同时要设定合法状态
out->Next[i]->if_end = out->fail->Next[i]->if_end ? : out->Next[i]->if_end;
}
Queue[tail++] = out->Next[i];
}
else
{
if (out == root) out->Next[i] = root;
else out->Next[i] = out->fail->Next[i];
}
}
}

  另外这个题要用到大数加法,在网上找了个很好的模板,以后就用这个了

  

  参考:http://blog.csdn.net/AndyTeen/article/details/45668121

     http://blog.csdn.net/scut_pein/article/details/22204681

     http://www.cnblogs.com/laiba2004/p/4004417.html

Match:Censored!(AC自动机+DP+高精度)(POJ 1625)的更多相关文章

  1. POJ 1625 Censored!(AC自动机+DP+高精度)

    Censored! Time Limit: 5000MS   Memory Limit: 10000K Total Submissions: 6956   Accepted: 1887 Descrip ...

  2. [POJ1625]Censored!(AC自动机+DP+高精度)

    Censored! Time Limit: 5000MS   Memory Limit: 10000K Total Submissions: 10824   Accepted: 2966 Descri ...

  3. POJ1625 Censored! —— AC自动机 + DP + 大数

    题目链接:https://vjudge.net/problem/POJ-1625 Censored! Time Limit: 5000MS   Memory Limit: 10000K Total S ...

  4. POJ1625 Censored!(AC自动机+DP)

    题目问长度m不包含一些不文明单词的字符串有多少个. 依然是水水的AC自动机+DP..做完后发现居然和POJ2778是一道题,回过头来看都水水的... dp[i][j]表示长度i(在自动机转移i步)且后 ...

  5. 对AC自动机+DP题的一些汇总与一丝总结 (2)

    POJ 2778 DNA Sequence (1)题意 : 给出m个病毒串,问你由ATGC构成的长度为 n 且不包含这些病毒串的个数有多少个 关键字眼:不包含,个数,长度 DP[i][j] : 表示长 ...

  6. HDU 2457 DNA repair(AC自动机+DP)题解

    题意:给你几个模式串,问你主串最少改几个字符能够使主串不包含模式串 思路:从昨天中午开始研究,研究到现在终于看懂了.既然是多模匹配,我们是要用到AC自动机的.我们把主串放到AC自动机上跑,并保证不出现 ...

  7. Ural 1158. Censored! 有限状态自动机+DP+大整数

    Ural1158 看上去很困难的一道题. 原文地址 http://blog.csdn.net/prolightsfxjh/article/details/54729646 题意:给出n个不同的字符,用 ...

  8. HDU2296 Ring(AC自动机+DP)

    题目是给几个带有价值的单词.而一个字符串的价值是 各单词在它里面出现次数*单词价值 的和,问长度不超过n的最大价值的字符串是什么? 依然是入门的AC自动机+DP题..不一样的是这题要输出具体方案,加个 ...

  9. HDU2457 DNA repair(AC自动机+DP)

    题目一串DNA最少需要修改几个基因使其不包含一些致病DNA片段. 这道题应该是AC自动机+DP的入门题了,有POJ2778基础不难写出来. dp[i][j]表示原DNA前i位(在AC自动机上转移i步) ...

随机推荐

  1. NOIP2010 引水入城 题解

    http://www.rqnoj.cn/problem/601 今天发现最小区间覆盖竟然是贪心,不用DP!于是我又找到这题出来撸了一发. 要找到最上面每个城市分别能覆盖最下面哪些城市,如果最下面有城市 ...

  2. 【活动】不用买书,不用花钱,可以免费看HTML5入门连载了

    清华大学出版社推出的<HTML 5网页开发实例详解>适合HTML 5开发初学者和前端开发工程师.本书一经上市,就获得了读者的一致好评,为感谢读者,推出本书的连载活动.   本书术新颖.与时 ...

  3. ECshop安装及报错解决方案总结

    一.安装ECshop ECShop是一款B2C独立网店系统 ,适合企业及个人快速构建个性化网上商店.系统是基于PHP语言及MYSQL数据库构架开发的跨平台开源程序.2006年3月推出以来1.0版以来, ...

  4. 安装 vue.js和第一个hello world

    一.在自己的项目文件中使用npm下载vue npm install vue 二.在文件中引入vue.js 三.第一个hello world 注:scritpt代码必须写在html代码的下面

  5. ajax 请求多张图片数据

    需求分析: 实现对网页上的图片进行显示.由于读取图片名称相同,而图片内容已发生改变.采用网上所用的要求浏览器不缓存的操作不生效 <META http-equiv="Pragma&quo ...

  6. ajax之get、post

    异步获取数据: <script type="text/javascript"> /** * [showstatus 获取数据] * @return {[type]} [ ...

  7. 高性能滚动 scroll 及页面渲染优化

    最近在研究页面渲染及web动画的性能问题,以及拜读<CSS SECRET>(CSS揭秘)这本大作. 本文主要想谈谈页面优化之滚动优化. 主要内容包括了为何需要优化滚动事件,滚动与页面渲染的 ...

  8. asp.net读写配置文件方法

    方法1: System.Collections.Specialized.NameValueCollection nvc = (System.Collections.Specialized.NameVa ...

  9. 新建Oracle数据库时,提示使用database control配置数据库时,要求在当前oracle主目录中配置监听程序

    新建一个oracle数据库时,当提示使用database control配置数据库时,要求在当前oracle主目录中配置监听程序等字样的时候,问题是那个监听的服务没有启动,解决方法如下: 打开cmd命 ...

  10. iOS开发——UI基础-按钮内边距,图片拉伸

    一.内边距 UIButton有三个属性,分别可以设置按钮以及内部子控件的内边距 1.contentEdgeInsets 如果是设置contentEdgeInsets, 会把UIImageView和UI ...