题目大意:给定一个模式串S,n个匹配串str,我们可以用匹配串覆盖模式串,每个匹配串可出现多次,每个前后相连的匹配串不能完全重叠,如果str[i]和str[j]相同,在算方案数时仍是不同的。strlen(S) <= 1000,n <= 200,strlen(str[i]) <= 200.
解题思路:第一次看到题目,没什么思路,其实是题目没看懂。后来再结合着样例把题目看懂了,觉得挺有规律的,但是规律怎么样想不到,也想不到怎么用KMP、或者字典树或者AC自动机。又看了下S的长度,可以用O(n^2)的复杂度过。然后就一直往这个复杂度的算法去想,觉得可以先建棵字典树,然后再进行操作。
先不管题目要求的那么多匹配要求,能发现一个性质:匹配到i位置,那他肯定是从前面的某个j位置而来,可以是0,1..i-1,似乎好像的确满足无后效行,那似乎好像的确可以用动态规划来解。现在假设我们从S中的j位置(和前一句话意义相同)开始从字典树中遍历,遍历到某个节点是某个字符串结束节点,此时匹配到S中的i位置,说明这一段可以匹配,那么从j开始,j、j+1、j+1....i-1找之前匹配过的方法数,累加起来就是在i处正确匹配的方法数。
dp[i]表示合法匹配到i位置的方法数,现在从j位置开始匹配,说明dp[j],dp[j+1],dp[j+2]...dp[i]都在之前j-1处匹配的时候计算过,那么本次dp[i]要怎么计算,可以让tot+=dp[j]、dp[j+1],dp[j+2]..dp[i-1],最后匹配到S的末尾后再加到dp[i].为什么不能用dp[i]直接加呢,因为要保证后效性,不然在算dp[i+1]的时候就乱套了。
如果大家还是迷糊,用草稿模拟下我给的第一个测试数据的计算过程,
答案是13.本题计算过程有可能超32位int,注意在计算的时候用上64位int。
测试数据:
10
AAAAA
4
A
AA
AAA
AAAA
AAAAAAAA
3
AA
AA
AA
AAAA
4
A
AA
AAA
AAAA
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MIN 210 #define MAX 1100 #define MOD 1000000007 struct node { int cnt; node *next[26]; }*root; int n,dp[MAX]; char dir[MAX][MIN],str[MAX]; node *CreateNode() { node *p; p = (node *) malloc (sizeof(node)); p->cnt = 0; for (int i = 0; i < 26; ++i) p->next[i] = NULL; return p; } void Release(node *p) { for (int i = 0; i < 26; ++i) if (p->next[i] != NULL) Release(p->next[i]); free(p); } void Insert(char *str) { int i = 0,k; node *p = root; while (str[i]) { k = str[i++] - 'A'; if (p->next[k] == NULL) p->next[k] = CreateNode(); p = p->next[k]; } p->cnt++; } void Query(char *str){ int i = 0,k; node *p = root; while (str[i]) { k = str[i] - 'A'; if (p->next[k] == NULL) break; p = p->next[k]; dp[i] = p->cnt; i++; } } void UpdateDP(char *str,int index) { int i = 0,k,j; long long tp[MAX],tot = 0; node *p = root; memset(tp,0,sizeof(tp)); while (str[i]) { k = str[i] - 'A'; if (p->next[k] == NULL) break; p = p->next[k]; if (p->cnt) tp[i+index] = ((long long)(tot * p->cnt)) % MOD; tot = (dp[index+i] + tot) % MOD; i++; } for (j = 0; j <= i + index; ++j) dp[j] = (tp[j] + dp[j]) % MOD; } int main() { int i,j,k,t; scanf("%d",&t); while (t--) { scanf("%s",str); scanf("%d",&n); root = CreateNode(); for (i = 0; i < n; ++i){ scanf("%s",dir[i]); if (strlen(dir[i]) == 1) continue; Insert(dir[i]); } memset(dp,0,sizeof(dp)); Query(str); for (i = 1; str[i]; ++i) UpdateDP(str+i,i); printf("%d\n",dp[i-1]%MOD); Release(root); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。