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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章