题意:给定一个串,然后有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
*/