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. Unity调试相关

    1.LOG处理 将所有LOG信息写入到文件,并设置部分LOG显示到屏幕上,总结成以下脚本,将其挂载在摄像机上即可. using UnityEngine; using System.Collection ...

  2. [转载]解析用户生命周期价值:LTV

    http://www.sykong.com/2014/07/23144 http://youxiputao.com/articles/1288 http://www.woshipm.com/opera ...

  3. R中的par()函数的参数

    把R中par()函数的主要参数整理了一下(另外本来还整理了每个参数的帮助文档中文解释,但是太长,就分类之后,整理为图表,excel不便放上来,就放了这些表的截图)

  4. HDOJ 3593 The most powerful force

    树形DP / 泛化物品的背包...可以去看09年徐持衡论文<浅谈几类背包问题> The most powerful force Time Limit: 16000/8000 MS (Jav ...

  5. 6.3.28微信需群主确认才可进群&发GIF动图功能内测开始了

    昨天下午有网友收到微信6.3.28新版内测邀请,不过这个内部体验目前貌似只对安卓手机开放,苹果的IOS系统还不支持,会提示“你当前使用的是非安卓设备,不建议下载安卓体验包,但你仍可邀请朋友尝鲜”.最新 ...

  6. 详解 iOS 上机题!附个人见解

    庸者的救赎2015-11-20 02:30:23 AFN那个使用的时候不需要弱引用的,因为从你的封装方式来看,那个block并不会被当前视图控制器持有,而是被manager持有了,所以不需要__wea ...

  7. ubutu安装搜狗

    1.下载deb文件 下载32位 wget "http://pinyin.sogou.com/linux/download.php?f=linux&bit=32" -O &q ...

  8. 解决IE6不支持position:fixed的bug

    /*完整代码 */ /* 除IE6浏览器的通用方法 */ .ie6fixedTL { position: fixed; left:; top:; } .ie6fixedBR { position: f ...

  9. Java NIO与IO的区别和比较

    传统的socket IO中,需要为每个连接创建一个线程,当并发的连接数量非常巨大时,线程所占用的栈内存和CPU线程切换的开销将非常巨大.使用NIO,不再需要为每个线程创建单独的线程,可以用一个含有限数 ...

  10. iOS开发——源代码管理——SVN

    一.源代码管理(svn)简介 01. 源代码管理工具概述 ======================================================================= ...