UVaLive3942

UVaLive3942

Description

給你一條由小寫字母組成的長字符串S (長度不超過300000 ),再給你N1N4000 )條短字符串Ci (每條長度不超過100 ),用Ci 組成S ,問有多少種方法,結果對20071027 取模。

如:SabcdCi 分別爲:a,b,ab,cd 時。那麼有a+b+cdab+cd 兩種方法。

多組測試數據。

Input

每組測試數據:

第一行:一個字符串,S

第二行:一個整數,N

接下來N 行:每行一字條符串Ci

Output

若干行:

每行格式爲:”Case i: ans ”(不含引號)。

Solution

看到方案數就可以想到用DP ,這題有點像揹包問題,用Fi 表示:從S0Si 有多少種組合方法。則:

Fi =sum{Filen(Cj) |Cj=FilenCJ...i }

顯然會超時,每次搜索一遍Ci 匹配一遍太耗時了。

這就要用到trie 了。

C 全部組織成一棵trie ,枚舉S 的起始點i ,在trie 上從Si 開始匹配,若匹配到Sj 時可以匹配到一個完整的Ck 就可以用Fjlen(Ck) 更新Fj 了。

因爲len(Ci) 不超過100 ,所以在trie 上匹配的次數不超過100 ,不會超時。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>

using namespace std;
const int maxL=300100;
const int maxl=110;
const int maxn=4010;

char str1[maxL],word[maxn][maxl];
int trie[maxn*maxl][30],sz,check[maxn*maxl],q,L; 
int n;
long long int f[maxL];
int T(char a)
{return int(a-'a');}

void maketrie(int x)
{
    int u=0,N=strlen(word[x]);

    for(int i=0;i<N;i++)
    {
        if(!trie[u][T(word[x][i])])
        {
            memset(trie[++sz],0,sizeof(trie[sz]));
            check[sz]=0;
            trie[u][T(word[x][i])]=sz;
        }
        u=trie[u][T(word[x][i])];
    }
    check[u]++;
}
void find(int x)
{
    int u=0;
    for(int i=1;trie[u][T(str1[x])]&&x<L;i++,x++)
    {
        u=trie[u][T(str1[x])];
        if(check[u])
            f[x+1]=(f[x+1]+f[x+1-i]*check[u])%20071027;
    }
}
void init()
{
    sz=0;
    memset(f,0,sizeof(f));
    memset(check,0,sizeof(check));
    memset(trie[0],0,sizeof(trie[0]));
    f[0]=1;
    q++;
}
int main()
{
    while(scanf("%s",str1)==1)
    {
        init();
        L=strlen(str1);
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%s",word[i]);
        for(int i=1;i<=n;i++) maketrie(i);
        for(int i=0;i<L;i++) find(i);
        printf("Case %d: %lld\n",q,f[L]);
    }
    return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章