版权声明:转载请注明出处哦~ https://blog.csdn.net/Cassie_zkq/article/details/89264054
题目地址:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1943(为什么我注册不了QAQ)
知识储备
Trie(前缀树/字典树):
保存字符串的集合,如图
ch[i][j]表示结点i编号为j的子结点,val[i]表示结点i的附加信息(如,val[i]>0表示结点i是单词结点)
题意
给出一个由S个不同单词组成的字典和一个长字符串。把这个字符串分解成若干个单词的连接(单词可以重复使用),有多少种方法?输出方法数除以20071027的余数
如,4个单词:a,b,cd,ab 则abcd有两种分解方法
解题思路
对给出的S个单词建立字典树,将给出的字符串反序,一定要反序!因为要找前缀!
状态转移方程
a,b,c,ab建立的字典树如下:涂色的⭕️表示该结点是单词结点
ac代码
如果代码有错欢迎指出!
#include <iostream>
#include <algorithm>
#include <string.h>
#include <ctype.h>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <sstream>
#define maxn 105
using namespace std;
typedef long long ll;
ll dp[maxn],ch[maxn][maxn],val[maxn];
ll sz,S,mod=20071027;
char w[300004],s[maxn];
void build(char *s,int v)
{
ll u=0,n=strlen(s);
for(ll i=0;i<n;i++)
{
ll c=s[i]-'a';
if(!ch[u][c])
{
val[sz]=0;
ch[u][c]=sz++;
}
u=ch[u][c];
}
val[u]=v;//单词结点
}
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
while(scanf("%s",w)!=EOF)
{
memset(dp,0,sizeof(dp));
memset(ch,0,sizeof(ch));
memset(val,0,sizeof(val));
sz=1;
ll len=strlen(w);
reverse(w,w+len);//反序
//cout<<w<<endl;
scanf("%lld",&S);
for(int i=0;i<S;i++)
{
scanf("%s",s);
build(s,1);//建字典树
}
for(int i=0;i<len;i++)
{
ll u=0;//从根开始
for(int j=i;j>=0;j--)
{
ll c=w[j]-'a';
if(ch[u][c])
u=ch[u][c];
else
break;//结点u的子结点没有s[j]
if(!val[u]) continue;//不是单词结点,继续找
if(j==0)
dp[i]=(dp[i]+1)%mod;
else
dp[i]=(dp[i]+dp[j-1])%mod;
}
//cout<<dp[i]<<endl;
}
printf("%lld\n",dp[len-1]);
}
return 0;
}