1. 程式人生 > >2.3.1 Longest Prefix 最長字首(字典樹)

2.3.1 Longest Prefix 最長字首(字典樹)

Description

在生物學中,一些生物的結構是用包含其要素的大寫字母序列來表示的。生物學家對於把長的序列分解成較短的(稱之為元素的)序列很感興趣。 如果一個集合 P 中的元素可以通過串聯(允許重複;串聯,相當於 Pascal 中的 “+” 運算子)組成一個序列 S ,那麼我們認為序列 S 可以分解為 P 中的元素。並不是所有的元素都必須出現。舉個例子,序列 ABABACABAAB 可以分解為下面集合中的元素: {A, AB, BA, CA, BBC} 序列 S 的前面 K 個字元稱作 S 中長度為 K 的字首。設計一個程式,輸入一個元素集合以及一個大寫字母序列,計算這個序列最長的字首的長度。

Input

輸入資料的開頭包括 1..200 個元素(長度為 1..10 )組成的集合,用連續的以空格分開的字串表示。字母全部是大寫,資料可能不止一行。元素集合結束的標誌是一個只包含一個 “.” 的行。集合中的元素沒有重複。接著是大寫字母序列 S ,長度為 1..200,000 ,用一行或者多行的字串來表示,每行不超過 76 個字元。換行符並不是序列 S 的一部分。

Output

只有一行,輸出一個整數,表示 S 能夠分解成 P 中元素的最長字首的長度。

Sample Input

A AB BA CA BBC

ABABACABAABC

Sample Output

11

 

 

題意也是不好理解啊。。。

意思就是:從第一個集合裡,選任意個元素組成一個字串,這些元素可以重複,也可以不選。問組成的這樣的字串在第二個串裡的最長字首是多少!


按照輸入的單詞構造字典樹,並且在每一個單詞的末尾節點標記。

對給定的模式串在字典樹上進行查詢。

查詢的過程是這樣的,從第一個字母遍歷到最後一個字母,每一次都做一次(從當前下標 i 到字串末尾)的查詢(或者說是匹配)。設一個新的陣列 is[ i ] 代表當前下標之前的子串能否滿足題目的最長字首。

那麼如果is[i]==true,就代表 i 之前的子串一定滿足題目條件的最長字首。

模擬一下過程:

現命名第二個串為模式串,第一個串裡的單詞為元素。

從下標0開始,發現元素A和AB在模式串裡,那麼標記 is[0]和is[1],代表暫時能夠組合成ABA這個字首,以此類推。

到了下標5的時候,發現is[4] 為1,那麼繼續匹配,標記is[6],但此時is[5]是不標記的,因為ABABAC這個串是組合不了的,但是ABABACA又能組合,所以標記is[6]......

通過上述過程我們可以發現,只要is[i-1]被標記,那麼就可以去字典樹上做一遍匹配。

最後找到最後一個is[i]=1的下標輸出i+1即可。


(還是比較裸的字典樹

此題還有很多種解法,dp,記憶化搜尋,ac自動機。這兒就不寫了(我可能不會寫- -!),因為師弟問,才做了這道題- -。

 

小插曲:一開始T了,後來發現是一開始因為字串的長度我每次都寫的strlen(str),後來改掉就過了2333


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#define memset(a,v)  memset(a,v,sizeof(a))
#define max(a,b)   (a>b?a:b)
#define min(a,b)   (a<b?a:b)
#define swap(a,b)  (a=a+b,b=a-b,a=a-b)
#define eps 1.0E-8
using namespace std;
const int MAXL(2*1e5);
const int INF(0x7f7f7f7f);
const int mod(1e9+7);
typedef long long int LL;
int trie[MAXL+50][30];
bool ed[MAXL+50];
bool is[MAXL+50];
char word[12];
char str[MAXL+50];
char s[80];
int tot,len;
void buildTree()
{
    int root=0;
    for(int i=0;i<strlen(word);i++)
    {
        int id=word[i]-'A';
        if(!trie[root][id])
            trie[root][id]=++tot;
        root=trie[root][id];
    }
    ed[root]=true;
}
void wordSearch(int k)
{
    int root=0;
    for(int i=k;i<len;i++)
    {
        int id=str[i]-'A';
        if(!trie[root][id])
            return;
        root=trie[root][id];
        if(ed[root]==true)
            is[i]=true;
    }
}
int main()
{
    while(scanf("%s",word))
    {
        if(word[0]=='.') break;
        buildTree();
    }
    while(~scanf("%s",s))
        strcat(str,s);
    len=strlen(str);
    wordSearch(0);
    for(int i=1;i<len;i++)
        if(is[i-1]==true)
            wordSearch(i);
    int ans=0;
    /*
    for(int i=0;i<len;i++)
        cout<<is[i]<<" ";
    cout<<endl;
    */
    for(int i=len-1;i>=0;i--)
        if(is[i]==true){
            ans=i+1;break;
        }
    cout<<ans<<endl;
}


/*
A AB BA CA BBC
.
ABABACABAABC
*/