51Node 2012 字符串的魅力:SAM

題目傳送門

題解:

由於k很小,顯然要在這裏搞一搞事情的。考慮SAM(由於要考慮所有的子串……就直接想SAM了……),由於每個節點保存的是一些後綴,而這個k是對前綴的限制,誒……經典套路了,把輸入串反過來,k就變成了對後綴的限制,那麼對於每個節點保存的這些後綴,長度小於k的暴力處理,長度大於k的O(1)處理,細節巨多……寫了好久。據說SA可以秒。。。

Code:


#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+100;
string s[1005];
vector<int> has[1005];
int bas[maxn];
int n,k,L,R;
const int mod = 1e9+7;
typedef pair<int,int> PII;
void do_hash(int x){
    has[x].resize(s[x].size()+1);
    has[x][s[x].size()]=0;
    for (int i=s[x].size()-1;i>=0;i--){
        has[x][i] = (26LL*has[x][i+1]+s[x][i]-'a')%mod;
    }
}
int get_hash(int x,int l,int r){
    return (1LL*has[x][l]-1LL*has[x][r+1]*bas[r-l+1]%mod+mod)%mod;
}
/*注意需要按l將節點基數排序來拓撲更新parent樹*/
struct Suffix_Automaton{
    //basic
    int nxt[maxn*2][26],fa[maxn*2],l[maxn*2];
    PII pos[maxn*2];
    int last,cnt;
    void clear(){
        last =cnt=1;
        fa[1]=l[1]=0;
        memset(nxt[1],0,sizeof nxt[1]);
    }
    void init(string s,int posI){
        for (int i=0;i<s.size();i++){
            int c = s[i]-'a';
            int p = last;
            int np = ++cnt;
            l[np] = l[p]+1;
            pos[np] = {posI,i};
            last = np;
            while (p&&!nxt[p][c])nxt[p][c] = np,p = fa[p];
            if (!p)fa[np]=1;
            else{
                int q = nxt[p][c];
                if (l[q]==l[p]+1)fa[np] =q;
                else{
                    int nq = ++ cnt;
                    l[nq] = l[p]+1;
                    pos[nq] ={posI,i};
                    memcpy(nxt[nq],nxt[q],sizeof (nxt[q]));
                    fa[nq] =fa[q];
                    fa[np] = fa[q] =nq;
                    while (nxt[p][c]==q)nxt[p][c] =nq,p = fa[p];
                }
            }
        }
    }
    int calc(int L,int R,int l,int r){
        if(r<L||R<l)return 0;
        if(l>=L&&l<=R)return min(r,R)-l+1;
        else return min(r,R)-L+1;
    }
    long long query(){
        long long ret =0;
        for (int i=2;i<=cnt;i++){
            int len = min(l[fa[i]],k);
            int V = get_hash(pos[i].first,pos[i].second-len+1,pos[i].second);
            if(l[fa[i]]<=k){
                for (int j=l[fa[i]]+1;j<=min(k,l[i]);j++){
                    V = (26LL*V+s[pos[i].first][pos[i].second+1-j]-'a')%mod;
                    if(V>=L&&V<=R)ret++;
                }
                if(l[i]>k){
                    int len = l[i]-k;
                    V++;V%=mod;
                    int VV = V+len-1;
                    if(VV>=mod){
                        ret+=calc(L,R,0,VV-mod);
                        ret+=calc(L,R,V,mod-1);
                    }else{
                        ret+=calc(L,R,V,VV);
                    }
                }
            }else{
                V +=(l[fa[i]]-k);
                V++;V%=mod;
                int VV = V+l[i]-l[fa[i]]-1;
                if(VV>=mod){
                    ret+=calc(L,R,0,VV-mod);
                    ret+=calc(L,R,V,mod-1);
                }else{
                    ret+=calc(L,R,V,VV);
                }
            }
        }
        return ret;
    }
}sam;
int main(){
#ifdef ONLINE_JUDGE
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
#endif
    bas[0]=1;
    for (int i=1;i<maxn;i++){
        bas[i] = 26LL*bas[i-1]%mod;
    }
    cin>>n>>k>>L>>R;
    sam.clear();
    for (int i=0;i<n;i++){
        cin>>s[i];
        reverse(s[i].begin(),s[i].end());
        do_hash(i);
        sam.last=1;
        sam.init(s[i],i);
    }
    cout<<sam.query()<<'\n';
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章