Match:Censored!(AC自动机+DP+高精度)(POJ 1625)

题目大意:给定一些字符,将这些字符组成一个固定长度的字符串,但是字符串不能包含一些禁词,问你有多少种组合方式。
这是一道好题,既然出现了“一些”禁词,那么这题肯定和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)的更多相关文章
- POJ 1625 Censored!(AC自动机+DP+高精度)
Censored! Time Limit: 5000MS Memory Limit: 10000K Total Submissions: 6956 Accepted: 1887 Descrip ...
- [POJ1625]Censored!(AC自动机+DP+高精度)
Censored! Time Limit: 5000MS Memory Limit: 10000K Total Submissions: 10824 Accepted: 2966 Descri ...
- POJ1625 Censored! —— AC自动机 + DP + 大数
题目链接:https://vjudge.net/problem/POJ-1625 Censored! Time Limit: 5000MS Memory Limit: 10000K Total S ...
- POJ1625 Censored!(AC自动机+DP)
题目问长度m不包含一些不文明单词的字符串有多少个. 依然是水水的AC自动机+DP..做完后发现居然和POJ2778是一道题,回过头来看都水水的... dp[i][j]表示长度i(在自动机转移i步)且后 ...
- 对AC自动机+DP题的一些汇总与一丝总结 (2)
POJ 2778 DNA Sequence (1)题意 : 给出m个病毒串,问你由ATGC构成的长度为 n 且不包含这些病毒串的个数有多少个 关键字眼:不包含,个数,长度 DP[i][j] : 表示长 ...
- HDU 2457 DNA repair(AC自动机+DP)题解
题意:给你几个模式串,问你主串最少改几个字符能够使主串不包含模式串 思路:从昨天中午开始研究,研究到现在终于看懂了.既然是多模匹配,我们是要用到AC自动机的.我们把主串放到AC自动机上跑,并保证不出现 ...
- Ural 1158. Censored! 有限状态自动机+DP+大整数
Ural1158 看上去很困难的一道题. 原文地址 http://blog.csdn.net/prolightsfxjh/article/details/54729646 题意:给出n个不同的字符,用 ...
- HDU2296 Ring(AC自动机+DP)
题目是给几个带有价值的单词.而一个字符串的价值是 各单词在它里面出现次数*单词价值 的和,问长度不超过n的最大价值的字符串是什么? 依然是入门的AC自动机+DP题..不一样的是这题要输出具体方案,加个 ...
- HDU2457 DNA repair(AC自动机+DP)
题目一串DNA最少需要修改几个基因使其不包含一些致病DNA片段. 这道题应该是AC自动机+DP的入门题了,有POJ2778基础不难写出来. dp[i][j]表示原DNA前i位(在AC自动机上转移i步) ...
随机推荐
- Python程序的常见错误(收集篇)
关于Python Python是一门解释性的,面向对象的,并具有动态语义的高级编程语言.它高级的内置数据结构,结合其动态类型和动态绑定的特性,使得它在快速应用程序开发(Rapid Applicatio ...
- Maven初级学习(三)常用命令
依赖关系查看 mvn dependency:list #列表形式展示依赖 mvn dependency:tree #层级关系展示依赖 mvn dependency:analyze #依赖分析 声明周期 ...
- IIS 您要访问的网页有问题,无法显示!
提示:您要访问的网页有问题,无法显示.HTTP 500 – 内部服务器错误.”的问题解决方案! IIS服务器出现错误的原因很多,请尝试以下操作:1.查看网站属性——文档看看启用默认文档中是否存在:in ...
- CentOS系统操作mysql的常用命令
MySQL名字的来历MySQL是一个小型关系型数据库管理系统,MySQL被广泛地应用在Internet上的中小型网站中.由于其体积小.速度快.总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了 ...
- 2012Chengdu B (快速组合数)
B - Candy Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit St ...
- 2013nanjignB
B - Poor Warehouse Keeper Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & ...
- "A transport-level error has occurred when sending the request to the server,指定的网络名不在可用"的解决办法
项目在外网服务器上运行的时候,遇到一个异常:"A transport-level error has occurred when sending the request to the ser ...
- eclipse pydev 跳转
[windows]-[Preference]-[Pydev]-[Interpreter-Python]-[Libraries]-system PYTHONPATH 全部加一遍,先加父目录,再加子目录.
- BZOJ 3364: [Usaco2004 Feb]Distance Queries 距离咨询
Description 一棵树,询问两点间距离. Sol 倍增. 方向没用. 没有然后了. Code /************************************************ ...
- Linux内核 TCP/IP、Socket参数调优
Linux内核 TCP/IP.Socket参数调优 2014-06-06 Harrison.... 阅 9611 转 165 转藏到我的图书馆 微信分享: Doc1: /proc/sy ...