Banned Patterns 計蒜客 - A1533

這個題,感覺特別神奇,感覺複雜度是正確的,但是給不出特別正確的證明。

題意:給你n個匹配串,給你m次詢問,然後問詢問串裏有沒有子串是某個匹配串的映射?

最開始就感覺多模式匹配肯定是ac自動機嘛,然後發現映射情況數太多不好處理,就想有沒有什麼方法能消除字母與字母之間的影響,想了想口胡了一種做法,就是把考慮每個字母上次出現的位置,然後表示成位置差,這樣一來每個串就可以o(1)的映射成一個唯一的數字串。

但是這樣轉化的話,首先是動態開店,不過感覺每個節點的指針不會太多,這個log還是很小的。再之後, 就感覺ac自動機的fail樹不是特別好建,因爲動態開點的,很可能我有的指針我的fail沒有。然後我腦子一熱就讓每個點繼承上一個點的狀態了,不是很會證這個複雜度,但是明顯能感覺出很作死的樣子,然後果然tle了。

去網上找了找題解,發現有一個大佬是差不多的思路,然後他是每次向上暴力跳fail的嘛,他也說給不出複雜度的證明。這個寫法是能ac的。

考慮了一些情況,感覺暴力跳的話應該是達不到(n^2)的,這裏給出一個不一定正確的想法。考慮只有一條鏈時的fail樹,大概應該是o(n)的,因爲在這一次我跳過的路徑,其他點不會再跳一遍。然後考慮n個串的時候,感覺上最差是o(nsqrt(n))的,但是給不出證明。然後題目保證匹配串只有5000個,也不知道是不是這裏的保證。

 

還有一個細節,因爲是靠記錄上一個出現的位置轉化的字符串,這樣在失配的時候很可能當前位置變成未出現過的了,要特判一下。

#include <bits/stdc++.h>

using namespace std;

#define N 200025
#define M 27
#define ll long long
#define mod 1000000007
#define go(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define inf 0x3f3f3f3f
#define ld long double
#define pii pair<int,int>
#define vi vector<int>
#define add(a,b) (a+=(b)%mod)%=mod
#define lowb(c,len,x) lower_bound(c+1,c+len+1,x)-c
#define uppb(c,len,x) upper_bound(c+1,c+len+1,x)-c
#define ls i*2+1
#define rs i*2+2
#define mid (l+r)/2
#define lson l,mid,ls
#define rson mid+1,r,rs
//#define root 1,n,0
#define ms(a,b) memset(a,b,sizeof a)
#define muti int T,cas=1;cin>>T;while(T--)
#define lll __int128
#define si short int
#define fi first
#define se second
#define l(x) (x&-x)
#define G(x) for( int i=h[x];i;i=eg[i].y )
map<int,int>::iterator it;
struct no{
    int en[N],len[N],fail[N],l,root,now;
    map<int,int>ch[N];
    int newnode(int dep){
        ch[l].clear();en[l]=0;len[l]=dep;
        return l++;
    }
    void init(){
        l=0;
        root=newnode(0);
    }
    int getlast(int u,int d){
        if(d>=len[u]+1)d=0;
        while(u!=root&&!ch[u].count(d)){
            u=fail[u];
            if(d>=len[u]+1)d=0;
        }
        return ch[u][d];
    }
    void inset(int d){
        //cout<<d<<endl;
        if(!ch[now].count(d))ch[now][d]=newnode(len[now]+1);
        //cout<<now<<' '<<l<<endl;
        now=ch[now][d];
        //cout<<now<<endl;
    }
    void build(){
        queue<int>q;
        fail[root]=0;
        it=ch[root].begin();
        while(it!=ch[root].end()){
            fail[it->se]=root;
            q.push(it->se);
            it++;
        }
        while(!q.empty()){
            int now=q.front();q.pop();
            en[now]|=en[fail[now]];
         //   cout<<now<<" "<<fail[now]<<"++++++++"<<endl;
            it=ch[now].begin();
            while(it!=ch[now].end()){
                fail[it->se]=getlast(fail[now],it->fi);
             //   cout<<it->fi<<' '<<it->se<<endl;
                q.push(it->se);
                it++;
            }
        }
    }
    int query(int d){
        if(d>len[now])d=0;
        now=getlast(now,d);
        return en[now];
    }
}ac;
int n,m,las[M];
char s[N];
int main()
{
    muti{
        scanf("%d",&n);
        ac.init();
        go(i,1,n){
            go(j,0,25)las[j]=0;
            scanf("%s",s+1);
            int len=strlen(s+1);
            ac.now=ac.root;
            go(j,1,len){
                if(!las[s[j]-'A'])ac.inset(0);
                else ac.inset(j-las[s[j]-'A']);
                las[s[j]-'A']=j;
            }
            ac.en[ac.now]=1;
        }
       // cout<<1111111111111<<endl;
        ac.build();
        scanf("%d",&m);
        printf("Case #%d:",cas++);
        go(i,1,m){
            go(j,0,25)las[j]=0;
            scanf("%s",s+1);
            int len=strlen(s+1),flag=0;
            ac.now=ac.root;
            go(j,1,len){
                if(!las[s[j]-'A'])flag=ac.query(0);
                else flag=ac.query(j-las[s[j]-'A']);
                las[s[j]-'A']=j;
                if(flag)break;
            }
            printf(" %c",flag?'Y':'N');
        }
        cout<<endl;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章