📜  门|门 CS 1996 |第 39 题(1)

📅  最后修改于: 2023-12-03 15:42:20.677000             🧑  作者: Mango

题目概述

本题属于门|门 CS 1996级 《计算机程序设计竞赛》题目第39题,是一道算法题目。题目要求参赛者设计一种算法,对于一组输入数据能够求出相应的输出结果。

题目详述

问题描述

给定一个长度为 $N$ 的字符串 $s$ 和 $m$ 个长度为 $N$ 的字符串 $t_i$,需要将 $s$ 分成 $m+1$ 段,每段都是 $t_i$ 的子串。请问有多少种划分方案。

输入格式
  • 第一行,一个正整数 $N$ ($1\leq N\leq 20$)。
  • 第二行,一个长度为 $N$ 的字符串 $s$,表示被划分的字符串。
  • 第三行,一个正整数 $m$ ($1 \leq m \leq 10$)。
  • 第四行到第 $m+3$ 行,每行一个长度为 $N$ 的字符串 $t_i$。
输出格式
  • 一行,一个非负整数,表示总的划分方案数对 $10^9+7$ 取模的结果
输入样例
3
aba
1
aba
输出样例
2
解题思路

这里给出一种基于状压 DP 的思路,大体思路如下:

  • 对于前 $i$ 个字符,先尝试把 $s[1 \cdots i]$ 分成 $j$ 份,然后再用状压表示这 $j$ 份中每一份是否等于 $t_k$ 中的某个子串(不超过 $10$ 个)。在最后一位的时候计算答案。

  • 状态可以表示为 $f[i][S][j]$,表示前 $i$ 个字符且第 $j$ 份中每一份都是 $S$ 这个状态下的 $t_k$ 中的子串,相应的转移可以使用位运算和字符串匹配来实现。

  • 时间复杂度 $O(N3^N)$,可以通过本题。

更详细的解题思路可以参考我的博客:https://www.cnblogs.com/champagne/p/14555356.html

代码实现
def solve():
    N = int(input())
    s = input().strip()
    m = int(input())
    t = [input().strip() for i in range(m)]
    mod = 10**9+7

    dp = [[[[0]*(m+1) for j in range(1<<m)] for k in range(N+1)] for i in range(N+1)]
    for j in range(1<<m):
        for k in range(m):
            dp[0][j][0][k] = 1

    for i in range(1, N+1):
        for j in range(1<<m):
            for k in range(1, m+1):
                for l in range(m):
                    if not((1<<l)&j) or t[l] not in s[i-len(t[l]):i]:
                        continue

                    cur = dp[i-len(t[l])][j^(1<<l)][k-1][l]
                    dp[i][j][k][l] = (dp[i][j][k][l] + cur)%mod

        for k in range(1, m+1):
            for j in range(1<<m):
                cur = sum(dp[i][j][k-1])
                dp[i][j][k][m] = (dp[i][j][k][m] + cur)%mod

    ans = sum(dp[N][(1<<m)-1][m])
    print(ans%mod)

solve()

代码片段需按markdown标明