0
点赞
收藏
分享

微信扫一扫

LA3942字典树+递推

题意:
     给你一个字典,最多4000个单词,每个单词长度最多是100,然后给你一个串,问你这个子串可以被那些单词组合的组合数,比如字典里有4个单词a b ab cd,然后给你一个串abcd则abcd = a+b+cd,ab+cd一共两种组合。输出组合数对20071027取余(白书上写错了写的是20071207)


思路:
       我们可以找到一个递推公式,d[i] = sum(i + len[x]),解释一下这个,d[i]表示的是以i个位置为开头的字符串的组合个数,就是[i,i+1,i+2..len-1],而x则是以i开头的那个串的前缀,这样就不难理解了吧,整体意思就是如果defg = 5,那么只要存在bc,就可以得到以a开头的abcdefg可以加上5了,然后就是优化时间,因为直接暴力写的话30000*4000*判断前缀匹配,时间复杂度接受不了,既然是前缀,我们可以想到字典树,我们可以把所有的4000个单词都放到字典里,然后在匹配的时候如果碰到单词末尾节点,直接就是找到满足条件,更新左右值,就行了,具体看代码,很容易理解。
PS不要把30000的那个字符串拆开放到字典树里,一开始我就是这么想的,结果还没敲完意识到这样内存会很大,很可能会爆内存,还有就是别忽视strlen这个函数的时间复杂度,TLE了一次。
      

#include<stdio.h>

#include<string.h>

#include<stdlib.h>



#define N 300000 + 10

#define MOD 20071027



typedef struct Tree

{

Tree *next[26];

int mk;

}Tree;



Tree root;

char Str[N];

long long dp[N];



void BuidTree(char *str)

{

int len = strlen(str);

Tree *p = &root ,*q;

for(int i = 0 ;i < len ;i ++)

{

int id = str[i] - 'a';

if(p -> next[id] == NULL)

{

q = (Tree *)malloc(sizeof(root));

q -> mk = 0;

for(int j = 0 ;j < 26 ;j ++)

q -> next[j] = NULL;

p -> next[id] = q;

p = p -> next[id];

}

else

p = p -> next[id];

}

p -> mk = 1;

}



void Query(char *str ,int ii ,int len)

{

dp[ii] = 0;

Tree *p = &root;

for(int i = ii ;i < len ;i ++)

{

int id = str[i] - 'a';

p = p -> next[id];

if(p == NULL) break;

if(p -> mk) dp[ii] = (dp[ii] + dp[i+1]) % MOD;

}

return ;

}



int main ()

{

int cas = 1 ,i ,n;

char tmp[105];

while(~scanf("%s" ,Str))

{

scanf("%d" ,&n);

for(i = 0 ;i < 26 ;i ++)

root.next[i] = NULL;

for(i = 1 ;i <= n ;i ++)

{

scanf("%s" ,tmp);

BuidTree(tmp);

}

int len = strlen(Str);

dp[len] = 1;

for(i = len - 1 ;i >= 0 ;i --)

{

Query(Str ,i ,len);//把len直接传下去,别在里面从求,会超时。

}

printf("Case %d: %lld\n" ,cas ++ ,dp[0]);

}

}

举报

相关推荐

0 条评论