POJ1625 Censored!

题目:

给一个N个字符组成的集合,用这些字符来构成长度为M的字符串,要求每个串的子串都不出现P个给定串的任意一个。求合法的字符串总数。

数据范围:1<=N<=50,1<=M<=50,0<=P<=10

算法分析

我们把串看做是一个一个字母添加到尾部构成的,那么一个串的后缀如果恰好是某个给定串的前缀,这个串就有可能发展成为一个非法串。

容易是哪个以一个比较简单的状态表示:f[i][j][k]表示递推到第i位,当前串的后缀是第j个给定串的长度为k的前缀的方案数。但是因为不同的给定串可能会出现相同的前缀,所以会出妯各种重复的情况,十分麻烦。

我们想到一种解决字符串问题的利器——AC自动机,具体的实现方法是Trie树,也就是字典树。我们把所有给定串插入Trie树,然后在树上进行递推。

f[i][j]代表递推到第i个字符,当前在自动机上j号节点的方案数。

如果下一个节点是k,直接把f[i][j]添加上f[i+1][k];如果为空,那么将当前前缀的首位去掉,再重新找对应节点,找不到就再删一位,一直到找到为止。

上面的方法缺点在于一出现为空的节点则时间代价就会变得很高。我们采取一种巧妙的方法解决这个问题。

对于自动机的第i个节点我们存一个pre[i],其中从根节点到pre[i]节点表示的字符串是从根节点到i节点表示的字符串的最长后缀。换句话说,如果从i找不到节点了,那么就从pre[i]找,还找不到就找pre[pre[i]],一直到根为止。

pre数组的计算方法与KMP的next数组非常类似,不断利用父节点信息就可以得到自己的信息。

最后的答案是所有非危险节点的方案数的和。危险节点有两种(其实危险节点上都是0),一种是叶节点(给定串的终点),或pre节点为危险节点的节点。因为遍历到危险节点的点上的方案必定是包含了给定串的方案,故不能记录这些。

经过这样处理的递推,复杂度可以降到O(NML),是一个非常优秀的算法

Tips:

这道题的答案非常大,需要使用高精度计算。

猜你喜欢

转载自www.cnblogs.com/bearteddy/p/10837252.html