1465 : 后缀自动机五·重复旋律8
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一段音乐旋律可以被表示为一段数构成的数列。
小Hi发现旋律可以循环,每次把一段旋律里面最前面一个音换到最后面就成为了原旋律的“循环相似旋律”,还可以对“循环相似旋律”进行相同的变换能继续得到原串的“循环相似旋律”。
小Hi对此产生了浓厚的兴趣,他有若干段旋律,和一部音乐作品。对于每一段旋律,他想知道有多少在音乐作品中的子串(重复便多次计)和该旋律是“循环相似旋律”。
输入
第一行,一个由小写字母构成的字符串S,表示一部音乐作品。字符串S长度不超过100000。
第二行,一个整数N,表示有N段旋律。接下来N行,每行包含一个由小写字母构成的字符串str,表示一段旋律。所有旋律的长度和不超过 100000。
输出
输出共N行,每行一个整数,表示答案。
样例输入
abac
3
a
ab
ca
样例输出
2
2
1
题目中已经有题解了,这里简略的讲一下,还是很简单的
还是先放一下这张经典的图:"aabbabd"的SAM:
先解决一个简单的问题:给你两个串S和T,求出T和S的最长公共子串(注意子串是要连续的)
当然先对串S求后缀自动机
之后对于串T的每个字符T[i],求出len[i] = 以i结尾的最长公共子串长度,u[i] = 以T[i]结尾的最长公共子串所在的SAM节点
那么如何线性求出所有的len[i] 和u[i] 呢?
很好办,初始u[0] = S,len = 0,之后对于当前字符T[k]
- 如果trans(u[k-1], T[k])!=NULL,则u[k] = trans(u[k-1], T[k]),len[k] = len[k-1]+1,搞定!
- 如果rans(u[k-1], T[k])==NULL,则u[k-1] = pre(u[k-1]),len[k-1] = longest(u[k-1]),继续执行步骤①直到满足为止或者回到后缀自动机的根节点S后仍然无法匹配
接着解决问题(2):给你两个串S和T,求出T的所有前缀分别在S中出现了多少次
只要对字符串T求出所有的u[i]和len[i],之后求出后缀自动机中每个节点的|endpos|(这个在这里有讲解)
答案就很明显了,对于当前前缀T[1..i],答案就是节点u[i]的|endpos|值
最后一个问题:给你两个串S和T,求出T的所有循环同构在S中总共出现了多少次
如果前两个问题都会做的话,这题就应该很容易有大致思路了:
设T原本长度为n,将T复制一遍拼在自身后面,之后求出T所有长度≥n的前缀T[1..i]在S中出现的次数之和就是答案
然后这样做肯定会有错误计算,所以还需要处理以下两种情况避免错误计算
- 前缀的长度很明显会≥n:只需沿着当前u[i]的Suffix Links不停回退,直到找到长度刚好=n的那段后缀T[i-n+1...i]所在的节点u[i]'即可
- 会出现完全相同的两段后缀,这个时候会重复: 这个就更好办了,标记一下所有的u[i],每个u[i]对答案的贡献显然不可能超过1次
#include<stdio.h>
#include<vector>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL long long
typedef struct Node
{
int len, pre;
int Next[26];
}Node;
Node tre[500005];
vector<int> G[500005];
int cnt, last, siz[500005], ans[500005], vis[500005];
char str[200005];
void Init()
{
cnt = last = 0;
memset(tre, 0, sizeof(tre));
tre[cnt++].pre = -1;
}
void Insert(char ch)
{
int p, q, now, rev;
p = last, now = cnt++;
tre[now].len = tre[last].len+1;
siz[now]++;
while(p!=-1 && tre[p].Next[ch-'a']==0)
{
tre[p].Next[ch-'a'] = now;
p = tre[p].pre;
}
if(p==-1)
tre[now].pre = 0;
else
{
q = tre[p].Next[ch-'a'];
if(tre[q].len==tre[p].len+1)
tre[now].pre = q;
else
{
rev = cnt++;
tre[rev] = tre[q];
tre[rev].len = tre[p].len+1;
tre[q].pre = tre[now].pre = rev;
while(p!=-1 && tre[p].Next[ch-'a']==q)
{
tre[p].Next[ch-'a'] = rev;
p = tre[p].pre;
}
}
}
last = now;
}
void SechSL(int u)
{
int i, v;
for(i=0;i<G[u].size();i++)
{
v = G[u][i];
SechSL(v);
siz[u] += siz[v];
}
ans[tre[u].len] = max(ans[tre[u].len], siz[u]);
}
int main(void)
{
char ch;
int n, i, Q, len, now, ans;
scanf("%s%d", str+1, &Q);
n = strlen(str+1);
Init();
for(i=1;i<=n;i++)
Insert(str[i]);
for(i=1;i<=cnt-1;i++)
G[tre[i].pre].push_back(i);
SechSL(0);
while(Q--)
{
scanf("%s", str+1);
n = strlen(str+1);
for(i=1;i<=n-1;i++)
str[i+n] = str[i];
ans = now = len = 0;
for(i=1;i<=n*2-1;i++)
{
ch = str[i]-'a';
while(now && tre[now].Next[ch]==0)
now = tre[now].pre, len = tre[now].len;
if(tre[now].Next[ch])
now = tre[now].Next[ch], len++;
if(len>=n)
{
while(len>=n && tre[tre[now].pre].len>=n)
now = tre[now].pre, len = tre[now].len;
}
if(len>=n && vis[now]!=Q+1000)
vis[now] = Q+1000, ans += siz[now];
}
printf("%d\n", ans);
}
return 0;
}