hihocoder1465(循环同构串在模式串中出现次数)

传送门

题目概要:

给定一个模式串 \(S\),然后给了 \(n\) 个字符串,求这 \(n\) 个串的循环同构在 \(S\) 中出现次数。
循环同构:取从左开始任意长度子串置于末尾。

每次把一段旋律里面最前面一个音换到最后面就成为了原旋律的“循环相似旋律”,还可以对“循环相似旋律”进行相同的变换能继续得到原串的“循环相似旋律”。

思路

对循环同构的处理我们很容易想到可以把相同的子串接到末尾,这样 \(T[i-n+1,i]\)就是T的同构。
求某个串在模式串中出现次数不难想到ac自动机,但是由于存在循环同构的原因,复杂度最坏会达到 \(O(n^2)\)
我们知道对于 \(SAM\) 中每个状态,\(endpos\) 就是这个状态中的子串出现的次数。
于是,我们先构造 \(S\)\(SAM\),并维护两个状态。\(u\),以 \(T[i]\)为结尾的最长公共前缀所属状态,\(l\) ,最长公共前缀的长度。初始时\(u=1,l=0\)
假设我们已知\(T[i-1]\)\(u,l\) ,如果\(trans[u][T[i]]!=0\) ,那么直接令\(u'=trans[u][T[i]],l'=l+1\)
如果 \(trans[u][T[i]]\) 不存在,我们沿着\(suffix-path(u \to S)\)找到存在的状态 \(v\),令 \(u=trans[v][T[i]],l=maxlen[v]+1\)
若始终不存在这样的状态,说明 \(T[i]\)\(S\) 中不存在,重新初始化 \(u=1,l=0\)
如果 \(l \geq n\),说明 \(lcp\)中含有同构,但由于同构只是 \(lcp\) 的后缀,此时的状态并非我们所求的状态。
我们继续沿着后缀链接找到最后一个满足 \(maxlen[v]>=n\) 的状态,此时的 \(endpos[v]\) 即同构串出现的次数。
最后,\(T\)的同构可能存在重复的情况,比如 \(aa\) 的同构只有 \(aa\) ,所以我们标记每次的最终状态\(v\) ,解决重复的问题。

代码

//#pragma GCC optimize(2)
//#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int N=1e6+5;
const ll mod=1e9+7;
const double eps=1e-5;
//const double pi=acos(-1);

#define ls p<<1
#define rs p<<1|1
int maxlen[N<<1],trans[N<<1][26],link[N<<1],tot=1,last=1;
int siz[N<<1],vis[N<<1],deg[N<<1];
inline void add(int id)
{
    int cur=++tot,p;
    siz[cur]++;
    maxlen[cur]=maxlen[last]+1;
    for(p=last;p&&!trans[p][id];p=link[p]) trans[p][id]=cur;
    if(!p) link[cur]=1;
    else
    {
        int x=trans[p][id];
        if(maxlen[x]==maxlen[p]+1) link[cur]=x;
        else
        {
            int y=++tot;
            maxlen[y]=maxlen[p]+1;
            for(int i=0;i<26;i++) trans[y][i]=trans[x][i];
            link[y]=link[x];
            for(;p&&trans[p][id]==x;p=link[p]) trans[p][id]=y;
            link[cur]=link[x]=y;
        }
    }
    last=cur;
}
void calc()
{
    for(int i=1;i<=tot;i++) deg[link[i]]++;
    queue<int>q;
    for(int i=1;i<=tot;i++)
        if(!deg[i]) q.push(i);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        siz[link[u]]+=siz[u];
        if(--deg[link[u]]==0) q.push(link[u]);
    }
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    string s;
    cin>>s;
    for(auto v:s) add(v-'a');
    calc();
    int _;
    cin>>_;
    while(_--)
    {
        cin>>s;
        int n=s.size();
        for(int i=0;i<n-1;i++) s+=s[i];
        int ans=0;
        int u=1,l=0;
        for(auto c:s)
        {
            int x=c-'a';
            while(u!=1&&!trans[u][x])
                u=link[u],l=maxlen[u];
            if(trans[u][x])
                u=trans[u][x],l++;
            else u=1,l=0;
            if(l>=n)
            {
                while(maxlen[link[u]]>=n)
                    u=link[u],l=maxlen[u];
            }
            if(l>=n&&vis[u]!=_+1)
                vis[u]=1+_,ans+=siz[u];
        }
        printf("%d\n",ans);
    }
    return 0;
}


posted @ 2020-04-09 12:51  Suiyue_Li  阅读(194)  评论(0编辑  收藏  举报