1 条题解
-
0
弗里兰语安全句子计数问题题解
这个问题涉及到字符串匹配、自动机和动态规划的综合应用,主要考察AC自动机和状态转移的思想。
问题分析
问题描述: 给定一个包含N个字符的字母表和P个被禁止的单词,计算长度为M的句子中不包含任何禁止单词的数量。
关键思路:
- 使用AC自动机预处理所有禁止单词
- 利用动态规划在AC自动机上进行状态转移
- 处理大数计算问题,确保结果正确
解题思路
-
AC自动机构建:
- 将所有禁止单词插入到Trie树中
- 构建失败指针,使自动机能够在匹配失败时跳转至正确位置
- 标记所有包含禁止单词的节点
-
动态规划状态转移:
- 状态定义:f[i][j] 表示长度为i的字符串,当前在AC自动机的节点j的安全句子数量
- 状态转移:对于每个节点和每个可能的字符,检查转移后的节点是否包含禁止单词
- 初始状态:f[0][root] = 1
- 结果:所有长度为M且不包含禁止单词的节点的状态之和
-
大数处理:
- 由于结果可能非常大,使用自定义的大整数类进行计算
- 实现大整数的加法和输出功能
代码实现分析
AC自动机部分:
struct Tree { int child[305], fail, flag; void clear() { memset(child, 0, sizeof(child)); fail = 0; flag = 0; } }; struct Aho_Corasick_Automaton { int cnt; Tree tree[maxn]; void init() { cnt = 1; memset(tree, 0, sizeof(tree)); } void insert(char s[]) { // 将禁止单词插入Trie树 } void buildfail() { // BFS构建失败指针 } };
动态规划部分:
BigInteger f[105][305], ans = 0; // 初始化 f[0][1] = 1; // 动态规划状态转移 for(int i = 1; i <= m; i++) { for(int j = 1; j <= ac.cnt; j++) { if(ac.tree[j].flag) continue; // 跳过包含禁止单词的节点 for(int k = 0; k < n; k++) { if(ac.tree[ac.tree[j].child[k]].flag) continue; // 转移后节点包含禁止单词 f[i][ac.tree[j].child[k]] += f[i-1][j]; } } } // 计算结果 for(int i = 1; i <= ac.cnt; i++) if(!ac.tree[i].flag) ans += f[m][i];
大整数类部分:
struct BigInteger { static const int BASE = 10000; static const int WIDTH = 4; vector<int> s; // 构造函数和赋值运算符 // 加法运算符重载 // 输出运算符重载 };
复杂度分析
- 时间复杂度:,其中C是AC自动机的节点数,N是字母表大小。
- 空间复杂度:,主要用于存储AC自动机和动态规划数组。
- 1
信息
- ID
- 626
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者