hihocoder 1465 : 后缀自动机五·重复旋律8(后缀自动机+最长公共子串)

版权声明:本文为博主原创文章,你们可以随便转载 https://blog.csdn.net/Jaihk662/article/details/82860974

 

1465 : 后缀自动机五·重复旋律8

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一段音乐旋律可以被表示为一段数构成的数列。

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

小Hi对此产生了浓厚的兴趣,他有若干段旋律,和一部音乐作品。对于每一段旋律,他想知道有多少在音乐作品中的子串(重复便多次计)和该旋律是“循环相似旋律”。

解题方法提示

输入

第一行,一个由小写字母构成的字符串S,表示一部音乐作品。字符串S长度不超过100000。

第二行,一个整数N,表示有N段旋律。接下来N行,每行包含一个由小写字母构成的字符串str,表示一段旋律。所有旋律的长度和不超过 100000。

扫描二维码关注公众号,回复: 3691211 查看本文章

输出

输出共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]

  1. 如果trans(u[k-1], T[k])!=NULL,则u[k] = trans(u[k-1], T[k]),len[k] = len[k-1]+1,搞定! 
  2. 如果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中出现的次数之和就是答案

然后这样做肯定会有错误计算,所以还需要处理以下两种情况避免错误计算

  1. 前缀的长度很明显会≥n:只需沿着当前u[i]的Suffix Links不停回退,直到找到长度刚好=n的那段后缀T[i-n+1...i]所在的节点u[i]'即可
  2. 会出现完全相同的两段后缀,这个时候会重复: 这个就更好办了,标记一下所有的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;
}

猜你喜欢

转载自blog.csdn.net/Jaihk662/article/details/82860974