1 条题解
-
0
算法分析与题解
问题分析
题目要求从 组字符串中每组选一个,形成长度为 的序列,满足相邻两层字符串是连续子串关系(长串去掉一个字符得到短串)。需要计算这样的序列总数。
核心算法:动态规划
这个代码采用了巧妙的 动态规划 + 子串快速判断 的方法:
1. 状态定义
设
f[i][j]表示以第 组第 个字符串结尾的合法序列数。2. 状态转移
对于第 组的字符串
s[i][j](长度 ),要判断它能否接在第 组的字符串后面,只需要检查:- 删除
s[i][j]的第一个字符后是否等于某个s[i-1][k] - 删除
s[i][j]的最后一个字符后是否等于某个s[i-1][k]
这是因为:
- 当长度差为 时,短串是长串的连续子串 ⇔ 长串删除一个字符得到短串
- 对于长串
abc,可能的子串只有ab(删除最后一个字符)或bc(删除第一个字符) - 因此只需检查这两种情况
3. 算法步骤
- 初始化:
f[1][i] = 1(第一层的每个字符串单独构成一条路径) - 递推:对于第 层第 个字符串
s[i][j]- 计算
S= 删除最后一个字符 - 计算
T= 删除第一个字符 - 遍历第 层的所有字符串
s[i-1][k] - 如果
s[i-1][k] == S或s[i-1][k] == T,则f[i][j] += f[i-1][k]
- 计算
- 答案:
ans = sum(f[N][i]) mod 1e9+7
时间复杂度分析
- 外层循环:
- 内层循环:
- 总复杂度:
- 在 时, 次操作
- 由于内层操作简单(字符串比较),实际可以接受
算法优化
代码的巧妙之处在于:
- 快速子串判断:无需枚举长串删除每个位置的情况,因为长度差为 时,连续子串只能是删除首字符或尾字符得到的结果
- 避免哈希:直接字符串比较,避免了哈希冲突问题
- 空间优化:只记录当前层和上一层的 DP 值(虽然代码中保留了所有层)
示例验证
以样例 1 为例:
3 2 a b ab bd abc abd- 第一层:
f[1][1]=1, f[1][2]=1 - 第二层:
ab:删除尾字符得a,删除首字符得b- 匹配
a:f[2][1] += f[1][1] = 1 - 匹配
b:f[2][1] += f[1][2] = 1,总计 2
- 匹配
bd:删除尾字符得b,删除首字符得d- 匹配
b:f[2][2] += f[1][2] = 1
- 匹配
- 第三层:
abc:删除尾字符得ab,删除首字符得bc- 匹配
ab:f[3][1] += f[2][1] = 2
- 匹配
abd:删除尾字符得ab,删除首字符得bd- 匹配
ab:f[3][2] += f[2][1] = 2 - 匹配
bd:f[3][2] += f[2][2] = 1,总计 3
- 匹配
- 答案:
f[3][1] + f[3][2] = 2 + 3 = 5
算法标签
#动态规划 #字符串处理 #组合计数 #子串匹配 #DAG路径计数 #递推优化 #模运算总结
这道题的解题关键在于:
- 将问题转化为分层 DAG 的路径计数问题
- 利用长度差为 1 时子串的特殊性,只需要检查删除首尾字符的情况
- 使用动态规划从底层向上递推计数
- 对 取模处理大数
代码简洁高效,充分利用了问题的特殊性质,避免了不必要的计算,是解决此类计数问题的典范。
- 删除
- 1
信息
- ID
- 5802
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者