LA 3942 Remember the Word(字典树/树上dp)

来源:互联网 发布:主播直播软件 编辑:程序博客网 时间:2024/05/23 19:11


题目,给出3w长度的字符串,一部字典 4000个长度不超过100的单词

把该字符串可以分解成x个字典的单词为一种合法方案

求出该该字符串的所有分解方案数,对20071027取模


思路:dp[i]表示 从第i个字符开始到结尾的一个子串

dp递推;dp[i]=sum(dp[ i+len(x)   ])  x为    子串S【i,结尾】的前缀


显然把单词做成字典树,每次对查询前缀x,看x在树种能遇到几个单词结束标记,由于单词长度最大100,所以查询最大复杂度o(100)

对于长度为n的字符串,要查询n次前缀,每次o(100),总复杂度 n*o(100)

也就是o(10^7)。。。数据比较弱。。所以69ms就过了


这次了解到一个就是 query函数 的 X的len由于是递减的,,所以只要在外面传进去就好。。。而之前手残地写了n=strlen(s)。。导致最后时间跑了2秒多。。。去掉后就是69ms了


#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <algorithm>#include <iostream>#include <queue>#include <map> #include <vector>using namespace std;#define inf 2147483647const int N = 300010; const int mod=20071027;const int maxnode = 400110; int dp[N];  char tm[N];//inline int idx(char c) {return c-'a';} int len; struct trie{int ch[maxnode][26];int val[maxnode];int sz;void init(){sz=1;val[0]=0;//可不需要memset(ch[0],0,sizeof(ch[0]));}int idx(char c) {return c-'a';}void insert(char *s ){int u=0,n=strlen(s),i;for (i=0;i<n;i++){int c=idx(s[i]);if (!ch[u][c]){memset(ch[sz],0,sizeof(ch[sz]));val[sz]=0;ch[u][c]=sz++;}u=ch[u][c];}val[u]=n;//标记当前单词权值(暂且当作字符串结尾标记)}/*int query_if(char *s)//查找当前单词是否存在,本题用不上{int u=0,n=strlen(s),i;for (i=0;i<n;i++){int c=idx(s[i]);if (!ch[u][c])return 0;else{u=ch[u][c];} }return val[u]; }*/int query(char *s,int p,int n)//查找该字符串的前缀能匹配上多少个单词{int u=0,i; //n=strlen(s);之前没把n传进来,而是脑残写下了这句,导致耗时2S+,去掉了69msfor (i=0;i<n;i++){int c=idx(s[i]);if (!ch[u][c])return 0; if (val[ch[u][c]]){if (p+i==len-1)dp[p]+=1;elsedp[p]=(dp[p]+dp[p+i+1])%mod; }u=ch[u][c]; }return 0;}};trie tp;int main(){char tmp[105];int cnt=1;while( scanf("%s", &tm)!=EOF){tp.init();    int n,i,j;scanf("%d",&n);len=strlen(tm);for (i=1;i<=n;i++){scanf("%s",&tmp);tp.insert(tmp);}for (i=len-1;i>=0;i--){ dp[i]=0;tp.query(tm+i,i,len-i);   }printf("Case %d: %d\n",cnt++,dp[0]%mod);//输出结果忘记取模wa几次}return 0;}


0 0
原创粉丝点击