CF235C-Cyclical Quest

题目

开始给出母串,多次询问一个串的所有不同循环串的在母串中的出现总次数。母串长和询问总长小于等于\(10^6\)

Input

baabaabaaa
5
a
ba
baa
aabaa
aaba

Output

7
5
7
3
5

分析

设计到子串的问题我们可以考虑后缀自动机。

问题就变成如何在后缀自动机中连续地匹配一个串的循环串。设需要匹配的串长为\(n\),我们首先把它倍长,放进自动机里匹配。如果匹配长度大于等于\(n\)就说明这个开头的位置匹配成功了。这时我们跳link,直到再跳一次就会令匹配长度小于\(n\),即当前点的出现次数就是这个循环串的出现次数,因为自动机的link相当于是在前面截掉一段。这样就可以\(O(n)\)解决每个匹配了。关于跳link的次数,每次跳link长度都会减一,而长度最多累加到\(2n\),所以得到这个复杂度。

后缀自动机的link是把前面一段截掉,这个性质很有用。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2e6+10; // 2e6+10
const int maxc=26;
char s[maxn];
struct SAM {
    int t[maxn][maxc],len[maxn],link[maxn],tot,last;
    int ord[maxn],sum[maxn],tim[maxn],tic;
    bool spe[maxn];
    SAM ():tot(1),last(1),tic(0) {}
    void add(int x) {
        int nw=++tot,i;
        spe[nw]=true;
        len[nw]=len[last]+1;
        for (i=last;i && !t[i][x];i=link[i]) t[i][x]=nw;
        if (i) {
            int p=t[i][x];
            if (len[p]==len[i]+1) link[nw]=p; else {
                int q=++tot;
                len[q]=len[i]+1;
                memcpy(t[q],t[p],sizeof t[p]);
                for (int j=i;j && t[j][x]==p;j=link[j]) t[j][x]=q;
                link[q]=link[p],link[p]=link[nw]=q;
            }
        } else link[nw]=1;
        last=nw;
    }
    void prepare() {
        for (int i=1;i<=tot;++i) ++sum[len[i]];
        for (int i=1;i<=tot;++i) sum[i]+=sum[i-1];
        for (int i=tot;i;--i) ord[sum[len[i]]--]=i;
        memset(sum,0,sizeof sum);
        for (int i=tot;i>1;--i) sum[link[ord[i]]]+=(sum[ord[i]]+=spe[ord[i]]);
        sum[0]=sum[1]=0;
    }
    int run(char s[],int n) {
        int ret=0,now=1,mat=0;
        ++tic;
        for (int i=1;i<(n<<1);++i) {
            int x=s[i]-'a';
            while (now!=1 && !t[now][x]) mat=len[now=link[now]];
            if (t[now][x]) now=t[now][x],++mat;
            while (len[link[now]]>=n) mat=len[now=link[now]];
            if (mat>=n && tim[now]!=tic) tim[now]=tic,ret+=sum[now];
        }
        return ret;
    }
} sam;
int main() {
#ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
#endif
    scanf("%s",s+1);
    int len=strlen(s+1);
    for (int i=1;i<=len;++i) sam.add(s[i]-'a');
    sam.prepare();
    int m;
    scanf("%d",&m);
    while (m--) {
        scanf("%s",s+1),len=strlen(s+1);
        memcpy(s+len+1,s+1,(sizeof s[0])*len);
        s[len<<1|1]='\0';
        int ans=sam.run(s,len);
        printf("%d\n",ans);
    }
    return 0;
}

posted @ 2017-07-09 09:11  permui  阅读(546)  评论(0编辑  收藏  举报