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 o 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
",ans);
    }
    return 0;
}


原文地址:https://www.cnblogs.com/Suiyue-Li/p/12666172.html