CF235C Cyclical Quest 题解
Description
定义文本串 \(S'\) 和 匹配串 \(x_i\) 同构:\(x_i\) 经过循环移位(可以不移)与 \(S'\) 相同。
给定一个 \(S\) 和许多 \(x_i\),询问对于每一个 \(x_i\) 有多少个 \(S\) 的子串 \(S'\) 与 \(x_i\) 同构。
Solution
先对 \(S\) 构建后缀自动机。
首先,题目中的问题可以转化为:设 \(l_i=len(x_i)\),将 \(x_i\) 自我复制一份后,有多少个 \(S\) 与 \(x_i\) 之间的长为 \(l_i\) 的公共子串。
接下来使用一个 SAM 上很重要的技巧:把 SAM 当作 AC 自动机一样进行字符串匹配。
匹配方法:对于当前已经匹配到的状态,如果有转移边,就直接转移并将当前匹配长度 \(+1\),否则一直跳 link 指针,并修改当前匹配长度为当前状态的 len。
于是直接匹配就好,注意同一个节点的贡献只能算一次,打个标记就好。
时间复杂度 \(O(\left| S \right| + \sum \left| x_i \right|)\)。
#include<bits/stdc++.h>
#define N 1000005
#define F(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
struct state{int to[26],link,cnt,len;}st[N<<1];
int sz,lst,n;
long long ans;
string s,x;
inline void insert(int c){
int cur=++sz,p=lst;
st[cur].len=st[lst].len+1,st[cur].cnt=1,lst=cur;
while(p!=-1&&!st[p].to[c])st[p].to[c]=cur,p=st[p].link;
if(p==-1)return;
int q=st[p].to[c];
if(st[p].len+1==st[q].len)return st[cur].link=q,void();
int clone=++sz;
st[clone]=st[q],st[clone].len=st[p].len+1,st[clone].cnt=0;
while(p!=-1&&st[p].to[c]==q)st[p].to[c]=clone,p=st[p].link;
st[cur].link=st[q].link=clone;
}
vector<int>t[N<<1];
inline void dfs(int u){for(int v:t[u])dfs(v),st[u].cnt+=st[v].cnt;}
int stk[N<<1],tt;
bool vis[N<<1];
int main(){
st[0].link=-1;
cin>>s>>n;
F(i,0,(int)s.length()-1)insert(s[i]-'a');
F(i,1,sz)t[st[i].link].push_back(i);
dfs(0);
while(n--){
cin>>x;
ans=0;
int u=0;
tt=0;
int len=0,L=x.length();
F(i,0,(int)x.length()-1){
int c=x[i]-'a';
while(u&&!st[u].to[c])u=st[u].link,len=st[u].len;
if(st[u].to[c])len++,u=st[u].to[c];
}
F(i,0,(int)x.length()-1){
int c=x[i]-'a';
while(u&&!st[u].to[c])u=st[u].link,len=st[u].len;
if(st[u].to[c])len++,u=st[u].to[c];
if(len>L){
len--;
if(len==st[st[u].link].len)u=st[u].link;
}
if(len==L)stk[++tt]=u,vis[u]=1;
}
F(i,1,tt)if(vis[stk[i]])vis[stk[i]]=0,ans+=st[stk[i]].cnt;//,cout<<stk[i]<<' '<<st[stk[i]].cnt<<' '<<st[stk[i]].len<<'\n';;
cout<<ans<<'\n';
}
}

浙公网安备 33010602011771号