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. Rsession让Java调用R更简单

    Rsession让Java调用R更简单 R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大. R语言作为统计学一门语言,一直在小众领域闪耀着光芒. ...

  2. CF456B Fedya and Maths 找规律

    http://codeforces.com/contest/456/problem/B CF#260 div2 B Fedya and Maths Codeforces Round #260 B. F ...

  3. sqlserver中将某数据库下的所有表字段名称为小写的改为大写

    declare @name varchar(50), @newname varchar(50),@colname varchar(50) declare abc cursor for select ( ...

  4. AD域服务器|两台DC无法进行复制同步

    注:本文由Colin撰写,版权所有!转载请注明原文地址,谢谢合作! 说明:前段时间公司两台域控出现了一些问题导致数据无法相互进行同步,DC之间也无法进行共享访问,网络用户无法通过计算机名映射的共享访问 ...

  5. 微信电脑版即将到来了 安装QQ浏览器微信版体验吧

    之前说过在手机上微信打字慢,tx最终还是想开了,最近TX邀请测试微信电脑版,想要尝鲜的朋友可以去exp.qq.com申请QQ浏览器微信版体验,不过体验将要结束了,相信正式版很快就要出来了.[微信网页版 ...

  6. 【bzoj2243】[SDOI2011]染色

    题目描述 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如"11 ...

  7. Android学习笔记(五)——活动的生命周期

    //此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 为了能写出流畅连贯的程序,我们需要了解一下活动的生命周期. 一.返回栈 Android 中的活动是可以层叠的. ...

  8. ionic隐藏tabs方法

    <ion-tabs ng-class="{'tabs-item-hide': $root.hideTabs}"> <!-- tabs --> </io ...

  9. [TimusOJ1057]Amount of Degrees

    [TimusOJ1057]Amount of Degrees 试题描述 Create a code to determine the amount of integers, lying in the ...

  10. 用js判断页面是否加载完毕

    用document.onreadystatechange的方法来监听状态改变, 然后用document.readyState == "complete"判断是否加载完成 docum ...