【2019 ICPC Latin American Regional】G.cutting pictures SAM

题意:给定一个串,然后有n个串,对每个串询问最少需要几个原串中的子串可以拼成目标串
分析:
对原串建立SAM,如果我们知道了目标串的前i位需要几个子串构成,记为dp[i],那么对于每个目标串,结果就是dp[lent],其中lent为每个目标串t的长度。那么转移很简单,dp[i] = dp[i-tmp]+1,其中tmp表示前i的字符组成的串的可以匹配的后缀的长度。
于是,对于每个串t,我们把每一位t[i]放在SAM中匹配即可…
详细的看代码应该能够理解。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+7;
struct node
{
	int ch[26];
	int len,fa;
}point[maxn<<1];
int las = 1,tot = 1;
char s[maxn],t[maxn];
int dp[maxn<<1];
void add(char c)
{
	int p = las;
	int np = las = ++tot;
	point[np].len = point[p].len+1;
	for(;p && !point[p].ch[c];p=point[p].fa) point[p].ch[c] = np;
	if(!p) point[np].fa = 1;
	else
	{
		int q = point[p].ch[c];
		if(point[q].len==point[p].len+1) point[np].fa = q;
		else
		{
			int nq = ++tot;
			point[nq] = point[q];
			point[nq].len = point[p].len+1;
			point[q].fa = point[np].fa = nq;
			for(;p && point[p].ch[c]==q;p=point[p].fa) point[p].ch[c] = nq;
		}
	}
}
void gao()
{
	int lent = strlen(t+1);
	int p = 1,tmp = 0;
	for(int i=1;i<=lent;i++)
	{
		char x = t[i]-'A';
		if(point[p].ch[x]) p = point[p].ch[x],tmp++;
		else
		{
			while(!point[p].ch[x] && p) p = point[p].fa;
			if(!p) p = 1,tmp = 0;
			else 
			{
				tmp = point[p].len+1;
				//cout<<"i="<<i<<",tmp="<<tmp<<endl;
				p = point[p].ch[x];
			}
		}
		if(tmp==0)
		{
			printf("-1\n");
			return;
		}
		dp[i] = dp[i-tmp]+1;
	}
	printf("%d\n",dp[lent]);
	return;
}
int main()
{
	scanf("%s",s+1);
	int lens = strlen(s+1);
	for(int i=1;i<=lens;i++) add(s[i]-'A');
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%s",t+1);
		gao();
	}
	return 0;
}
/*
MONTEVIDEO
4
DEMONIO
MONTE
EDIT
WON

SANTIAGO
3
TITA
SANTIAGO
NAS

*/
发布了159 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/KIKO_caoyue/article/details/103216909