UVa:1401 Remember the Word(字典树)

字典树,第一次写。还需要递推。

dp【i】=sum{dp【i+len(x)】}x是str【i……L】的前缀长度。

朴素的想法是枚举所有x,然后判断是否是str【i……L】的前缀,这是时间复杂度大约是30000*4000*比较的时间,会超时。

利用tire减少了不必要的比较,使得可以快速找到所有的x,即每次找到的都是str【i……L】的前缀,这样至多是30000*100。

关于字典树。用数组ch【i】【j】保存的是以i根字符是j的结点编号,val【u】保存的是第u个结点的附加信息。这个题里val【】非零表示是某个单词的终止。

sz表示的时候结点个数,在建树过程中这个值是变大的,而且建树之前,如ch【】【】,val【】对应结点都应该清空。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#define ll long long
#define INF 200000000
#define MOD 20071027
#define MAXN 5005
using namespace std;
int dp[300005];
struct Tire
{
    int ch[4005*100][26],val[4005*100];
    int sz;
    int idx(char c)
    {
        return c-'a';
    }
    void Reset()
    {
        sz=1;
        memset(ch,0,sizeof(ch));
        memset(val,0,sizeof(val));
    }
    void Insert(char *str,int v)
    {
        int u=0;
        for(int i=0; str[i]; ++i)
        {
            int c=idx(str[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]=v;
    }
    void solve(int x,char *str)
    {
        for(int i=x,u=0; str[i]; ++i)
        {
            int c=idx(str[i]);
            if(!ch[u][c]) return ;
            u=ch[u][c];
            if(val[u])  dp[x]=(dp[x]+dp[i+1])%MOD;
        }
    }
};
char str[300005],word[105];
Tire tree;
int main()
{
    int kase=0;
    while(scanf("%s",str)!=EOF)
    {
        int n;
        tree.Reset();
        memset(dp,0,sizeof(dp));
        scanf("%d",&n);
        for(int i=0; i<n; ++i)
        {
            scanf("%s",word);
            tree.Insert(word,1);
        }
        dp[strlen(str)]=1;
        for(int i=strlen(str)-1; i>=0; --i)
            tree.solve(i,str);
        printf("Case %d: %d\n",++kase,dp[0]);
    }

    return 0;
}


 

你可能感兴趣的:(字典树)