BUAA 533 nanae 是弱小灰(SAM)

acm_cxlove發表於2013-04-17

轉載請註明出處,謝謝http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove

題意 :給出一個A串,給出若干B串,問A中有多少個不相同的子串是以某個B串為字尾的。

Dshawn找我出個SAM,為了nanae的生日賽。
無聊的跨專業課,就出了這個個題吧~~~~~
對於SAM來說,解法比較簡單。
首先將A串和B串都反向之後,字尾便為字首,題目轉變為A串中有多少個不相同的子串是以某個B串為字首的。
那麼以A串建立SAM之後,首先預處理一下,對於A中的每一個子串,存在多少個不同的後繼。這就是一個簡單的DAG上的DP。得到的結果其實就是,以當前子串為字首的不同的子串有多少個  (= =這樣說的話,問題就解決了。
遍歷每一個B串,如果A中存在這個子串,那麼就統計一下這個子串有多少個後繼,疊加一下就行了。
這樣明顯有個問題,便是存在重複子串。
大概 就是如果某個結點被統計過,那麼他後繼中的所有結點的後繼,都已經被包括其中。
那麼其中一種處理方法便是,處理完之後,把可達的結點標記一下,然後通過DAG關係,依次處理,而且將所有後繼中的標記撤消。

那麼另外一種處理方法便是,問題的源由在於,B串中某些串是某個串的字首之類的。那麼將B按字典序排序之後,依次處理,如果某個B經過了已經被標記過的結點,那麼直接退出。

AekdyCoin的做法是SA,巨巨真可怕,不過不得不說SA的做法好麻煩,耗時也多。ORZ


#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define LL long long
using namespace std;
const int N=210005;
struct Node{
    char word[105];
    bool operator<(const Node n)const{
        return strcmp(word,n.word)<0;
    }
}a[1005];
struct SAM  {
    SAM *pre,*son[26];
    int f,len;
    LL cnt;
}*root,*tail,que[N],*b[N];
char str[N];
int cnt[N];
int tot=0;
int c[N];
int n;
void add(int c,int l){
    SAM *p=tail,*np=&que[tot++];
    np->len=l;
    while(p&&p->son[c]==NULL) p->son[c]=np,p=p->pre;
    if(p==NULL) np->pre=root;
    else{
        SAM *q=p->son[c];
        if(p->len+1==q->len) np->pre=q;
        else{
            SAM *nq=&que[tot++];
            *nq=*q;
            nq->len=p->len+1;
            np->pre=q->pre=nq;
            while(p&&p->son[c]==q) p->son[c]=nq,p=p->pre;
        }
    }
    tail=np;
}
LL ans=0;
int main(){
//    freopen("1.in","r",stdin);
//    freopen("output.txt","w",stdout);
    root=tail=&que[tot++];
    scanf("%s",str);
    int l=strlen(str);
    for(int i=0;i<l/2;i++) swap(str[i],str[l-1-i]);
    for(int i=0;str[i];i++) add(str[i]-'a',i+1);
    for(int i=0;i<tot;i++) c[que[i].len]++;
    for(int i=1;i<tot;i++) c[i]+=c[i-1];
    for(int i=0;i<tot;i++) b[--c[que[i].len]]=&que[i];
    for(int i=0;i<tot;i++) que[i].cnt=1;
    for(int i=tot-1;i>=0;i--){
        SAM *p=b[i];
        for(int j=0;j<26;j++){
            if(p->son[j])
                p->cnt+=p->son[j]->cnt;
        }
    }
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%s",a[i].word);
        int l=strlen(a[i].word);
        for(int j=0;j<l/2;j++)
            swap(a[i].word[j],a[i].word[l-1-j]);
    }
    sort(a,a+n);
    for(int j=0;j<n;j++){
        SAM *p=root;
        bool flag=true;
        for(int i=0;a[j].word[i]&&flag;i++){
            int s=a[j].word[i]-'a';
            if(p->son[s]==NULL) flag=false;
            else{
                if(p->son[s]->f) flag=false;
                p=p->son[s];
            }
        }
        if(flag){
            ans+=p->cnt;
            p->f=1;
        }
    }
    printf("%lld\n",ans);
    return 0;
}


相關文章